Full Code of Roave/no-floaters for AI

1.16.x f29123d0fb07 cached
41 files
40.2 KB
10.9k tokens
70 symbols
1 requests
Download .txt
Repository: Roave/no-floaters
Branch: 1.16.x
Commit: f29123d0fb07
Files: 41
Total size: 40.2 KB

Directory structure:
gitextract_9qceq59d/

├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── continuous-integration.yml
│       └── release-on-milestone-closed.yml
├── .gitignore
├── .laminas-ci.json
├── LICENSE
├── README.md
├── SECURITY.md
├── composer-require-checker.json
├── composer.json
├── infection.json.dist
├── logo/
│   └── roave-no-floaters.blend
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── psalm.xml
├── renovate.json
├── rules.neon
├── src/
│   ├── DisallowFloatAssignedToVariableRule.php
│   ├── DisallowFloatEverywhereRule.php
│   ├── DisallowFloatInFunctionSignatureRule.php
│   ├── DisallowFloatInMethodSignatureRule.php
│   ├── DisallowFloatPropertyTypeRule.php
│   └── FloatTypeHelper.php
└── tests/
    ├── asset/
    │   ├── assign.php
    │   ├── expr.php
    │   ├── function.php
    │   ├── functionNotAutoloaded.php
    │   ├── functionWithInterpolatedFloatAndNonFloatParameters.php
    │   ├── functionWithoutNamespace.php
    │   ├── method.php
    │   ├── methodWithConditionalReturnType.php
    │   ├── methodWithInterpolatedFloatAndNotFloatParameters.php
    │   └── property.php
    └── src/
        ├── DisallowFloatAssignedToVariableRuleTest.php
        ├── DisallowFloatEverywhereRuleTest.php
        ├── DisallowFloatInFunctionSignatureRuleTest.php
        ├── DisallowFloatInMethodSignatureRuleTest.php
        ├── DisallowFloatPropertyTypeRuleTest.php
        └── ScopeWithNodeCallbackInvoker.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
logo export-ignore
tests export-ignore
.gitignore export-ignore
.gitattributes export-ignore
.travis.yml export-ignore
composer-require-checker.json export-ignore
export-ignore export-ignore
phpcs.xml.dist export-ignore
phpstan.neon.dist export-ignore
phpunit.xml.dist export-ignore


================================================
FILE: .github/FUNDING.yml
================================================
tidelift: "packagist/roave/no-floaters"


================================================
FILE: .github/workflows/continuous-integration.yml
================================================
# See https://github.com/laminas/laminas-continuous-integration-action
# Generates a job matrix based on current dependencies and supported version
# ranges, then runs all those jobs
name: "Continuous Integration"

on:
  pull_request:
  push:

jobs:
  matrix:
    name: Generate job matrix
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.matrix.outputs.matrix }}
    steps:
      - name: Gather CI configuration
        id: matrix
        uses: laminas/laminas-ci-matrix-action@1.33.0

  qa:
    name: QA Checks
    needs: [ matrix ]
    runs-on: ${{ matrix.operatingSystem }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJSON(needs.matrix.outputs.matrix) }}
    steps:
      - name: ${{ matrix.name }}
        uses: laminas/laminas-continuous-integration-action@1.43.0
        env:
          "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
          "INFECTION_DASHBOARD_API_KEY": ${{ secrets.INFECTION_DASHBOARD_API_KEY }}
          "STRYKER_DASHBOARD_API_KEY": ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
        with:
          job: ${{ matrix.job }}


================================================
FILE: .github/workflows/release-on-milestone-closed.yml
================================================
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions

name: "Automatic Releases"

on:
  milestone:
    types:
      - "closed"

jobs:
  release:
    name: "GIT tag, release & create merge-up PR"
    runs-on: ubuntu-latest

    steps:
      - name: "Checkout"
        uses: "actions/checkout@v6"

      - name: "Release"
        uses: "laminas/automatic-releases@v1"
        with:
          command-name: "laminas:automatic-releases:release"
        env:
          "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
          "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
          "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
          "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}

      - name: "Create Merge-Up Pull Request"
        uses: "laminas/automatic-releases@v1"
        with:
          command-name: "laminas:automatic-releases:create-merge-up-pull-request"
        env:
          "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
          "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
          "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
          "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}

      - name: "Create and/or Switch to new Release Branch"
        uses: "laminas/automatic-releases@v1"
        with:
          command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
        env:
          "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
          "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
          "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
          "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}

      - name: "Bump Changelog Version On Originating Release Branch"
        uses: "laminas/automatic-releases@v1"
        with:
          command-name: "laminas:automatic-releases:bump-changelog"
        env:
          "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
          "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
          "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
          "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}

      - name: "Create new milestones"
        uses: "laminas/automatic-releases@v1"
        with:
          command-name: "laminas:automatic-releases:create-milestones"
        env:
          "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
          "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
          "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
          "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}


================================================
FILE: .gitignore
================================================
/vendor
.phpunit.cache
.phpunit.result.cache

================================================
FILE: .laminas-ci.json
================================================
{
  "extensions": [
    "pcov"
  ],
  "exclude": [
    {"name": "Infection"}
  ],
  "additional_checks": [
    {
      "name": "Infection (PCOV)",
      "job": {
        "php": "@lowest",
        "dependencies": "locked",
        "command": "./vendor/bin/roave-infection-static-analysis-plugin"
      }
    }
  ]
}

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) Roave, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# roave/no-floaters

![`roave/no-floaters`](logo/roave-no-floaters.png)

[![Latest Stable Version](https://poser.pugx.org/roave/no-floaters/v/stable.png)](https://packagist.org/packages/roave/no-floaters)

This library is a [PHPStan](https://github.com/phpstan/phpstan) plugin
that disallows:

* declaration of `float` properties
* `float` method parameters
* `float` method return types
* assignment of `float` values to variables or properties

The reason for this restriction is that rounding errors coming
from floating point arithmetic operations are not acceptable in
certain business logic scenario, such as dealing with money,
evaluating exam results, rocket science, etc.

An example of such problems can be seen with the following typical
[example](https://3v4l.org/MJqJe):

```php
var_dump((0.7 + 0.1) === 0.8); // output: bool(false)
```

This can mean no trouble at all, or a lot of trouble, depending
on how many numbers you are running through your system, so it
is advisable to avoid `float` for domains where rounding can
potentially lead to trouble.

`float` is still perfectly acceptable in many programming contexts,
and this ruleset should only be applied where it is critical not
to introduce rounding errors.

## Installation

```sh
composer require --dev roave/no-floaters
```

## Configuration

In your `phpstan.neon` configuration, add following section:

```neon
includes:
    - vendor/roave/no-floaters/rules.neon
```

Optionally, you can configure the library to disallow any
`float`-producing expression at all, by adding following to your
`phpstan.neon`:

```neon
parameters:
    disallowFloatsEverywhere: true
```

If the above is enabled, given the following `example-file.php`
contents:

```php
<?php

$a = 1 / 3;
```

You should get something like following:

```cli
vendor/bin/phpstan analyse example-file.php -l 7
 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ -----------------------------------------------------
  Line   example-file.php
 ------ -----------------------------------------------------
  3      Cannot assign float to $a - floats are not allowed.
 ------ -----------------------------------------------------


 [ERROR] Found 1 error

```

## roave/no-floaters for enterprise

Available as part of the Tidelift Subscription.

The maintainers of roave/no-floaters and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-roave-no-floaters?utm_source=packagist-roave-no-floaters&utm_medium=referral&utm_campaign=enterprise&utm_term=repo).

If you need help with setting up this library in your project,
you can contact us at <team@roave.com> for consulting/support.


================================================
FILE: SECURITY.md
================================================
## Security contact information

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.


================================================
FILE: composer-require-checker.json
================================================
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "void",
        "object",
        "PHPStan\\Analyser\\Scope",
        "PHPStan\\Node\\Printer\\Printer",
        "PHPStan\\Reflection\\FunctionReflection",
        "PHPStan\\Reflection\\MethodReflection",
        "PHPStan\\Reflection\\ParametersAcceptor",
        "PHPStan\\Reflection\\ReflectionProvider",
        "PHPStan\\Rules\\Rule",
        "PHPStan\\Rules\\RuleErrorBuilder",
        "PHPStan\\ShouldNotHappenException",
        "PHPStan\\Type\\FloatType",
        "PHPStan\\Type\\LateResolvableType",
        "PHPStan\\Type\\MixedType",
        "PHPStan\\Type\\NeverType",
        "PHPStan\\Type\\Type",
        "PHPStan\\Type\\VerbosityLevel"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Reflection",
        "SPL",
        "standard",
        "mbstring"
    ]
}


================================================
FILE: composer.json
================================================
{
    "name": "roave/no-floaters",
    "type": "phpstan-extension",
    "description": "PHPStan Rules to Disallow Float proliferation in contexts where IEEE-754 rounding errors are not acceptable",
    "license": [
        "MIT"
    ],
    "require": {
        "php": "~8.3.0 || ~8.4.0 || ~8.5.0",
        "nikic/php-parser": "^5.7.0",
        "phpstan/phpstan": "^2.1.39"
    },
    "require-dev": {
        "doctrine/coding-standard": "^14.0.0",
        "maglnet/composer-require-checker": "^4.20.0",
        "phpstan/phpstan-phpunit": "^2.0.16",
        "phpstan/phpstan-strict-rules": "^2.0.10",
        "phpunit/phpunit": "^12.5.11",
        "psalm/plugin-phpunit": "^0.19.5",
        "roave/infection-static-analysis-plugin": "^1.43.0",
        "squizlabs/php_codesniffer": "^4.0.1",
        "vimeo/psalm": "^6.15.1"
    },
    "autoload": {
        "psr-4": {
            "Roave\\PHPStan\\Rules\\Floats\\": "src/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/asset"
        ],
        "psr-4": {
            "Roave\\PHPStanTest\\Rules\\Floats\\": "tests/src"
        }
    },
    "config": {
        "allow-plugins": {
            "infection/extension-installer": false,
            "dealerdirect/phpcodesniffer-composer-installer": true
        },
        "platform": {
            "php": "8.3.99"
        },
        "sort-packages": true
    }
}


================================================
FILE: infection.json.dist
================================================
{
    "$schema": "vendor/infection/infection/resources/schema.json",
    "source": {
        "directories": [
            "src"
        ]
    },
    "logs": {
        "text": "php://stderr",
        "github": true
    },
    "mutators": {
        "@default": true
    },
    "minMsi": 100,
    "minCoveredMsi": 100
}

================================================
FILE: phpcs.xml.dist
================================================
<?xml version="1.0"?>
<ruleset
    name="PHPStan Disallow Float Rules"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd"
>
    <rule ref="Doctrine">
        <exclude name="Generic.Files.LineLength.TooLong"/>
    </rule>

    <file>src</file>
    <file>tests/src</file>
</ruleset>


================================================
FILE: phpstan.neon.dist
================================================
includes:
	- vendor/phpstan/phpstan-phpunit/extension.neon
	- vendor/phpstan/phpstan-phpunit/rules.neon
	- vendor/phpstan/phpstan-strict-rules/rules.neon
	- rules.neon

parameters:
	paths:
		- %currentWorkingDirectory%/src
		- %currentWorkingDirectory%/tests/src
	level: 8



================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
        colors="true"
        failOnAllIssues="true"
        beStrictAboutChangesToGlobalState="true"
        beStrictAboutOutputDuringTests="true"
        beStrictAboutCoverageMetadata="true"
        cacheDirectory=".phpunit.cache">
  <testsuites>
    <testsuite name="PHPStan Disallow Float Test Suite">
      <directory>./tests/src</directory>
    </testsuite>
  </testsuites>
  <coverage ignoreDeprecatedCodeUnits="true" disableCodeCoverageIgnore="true"/>
  <source>
    <include>
      <directory suffix=".php">src</directory>
    </include>
  </source>
</phpunit>


================================================
FILE: psalm.xml
================================================
<?xml version="1.0"?>
<psalm
    errorLevel="1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="https://getpsalm.org/schema/config"
    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
    <plugins>
        <pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
    </plugins>

    <projectFiles>
        <directory name="src" />
        <directory name="tests" />
        <ignoreFiles>
            <directory name="vendor" />
            <directory name="tests/asset" />
        </ignoreFiles>
    </projectFiles>
    
    <issueHandlers>
        <MissingOverrideAttribute errorLevel="suppress">
            <!-- the #[Override] attribute is mostly useless -->
        </MissingOverrideAttribute>
        <PossiblyUnusedReturnValue>
            <errorLevel type="suppress">
                <!-- these files are visitors, and their return values are only used by the\
                     visitor implementation in PHPStan itself -->
                <file name="src/DisallowFloatInMethodSignatureRule.php"/>
                <file name="src/DisallowFloatPropertyTypeRule.php"/>
            </errorLevel>
        </PossiblyUnusedReturnValue>
    </issueHandlers>
</psalm>


================================================
FILE: renovate.json
================================================
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "local>Ocramius/.github:renovate-config"
  ]
}


================================================
FILE: rules.neon
================================================
rules:
	- Roave\PHPStan\Rules\Floats\DisallowFloatAssignedToVariableRule
	- Roave\PHPStan\Rules\Floats\DisallowFloatInFunctionSignatureRule
	- Roave\PHPStan\Rules\Floats\DisallowFloatInMethodSignatureRule
	- Roave\PHPStan\Rules\Floats\DisallowFloatPropertyTypeRule

parameters:
	disallowFloatsEverywhere: false

conditionalTags:
	Roave\PHPStan\Rules\Floats\DisallowFloatEverywhereRule:
		phpstan.rules.rule: %disallowFloatsEverywhere%

services:
	-
		class: Roave\PHPStan\Rules\Floats\DisallowFloatEverywhereRule

parametersSchema:
	disallowFloatsEverywhere: bool()


================================================
FILE: src/DisallowFloatAssignedToVariableRule.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStan\Rules\Floats;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\Printer\Printer;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\VerbosityLevel;

use function sprintf;

/** @implements Rule<Node> */
final class DisallowFloatAssignedToVariableRule implements Rule
{
    public function __construct(private Printer $printer)
    {
    }

    public function getNodeType(): string
    {
        return Node::class;
    }

    /**
     * {@inheritDoc}
     */
    public function processNode(Node $node, Scope $scope): array
    {
        if (! $node instanceof Node\Expr\AssignOp && ! $node instanceof Node\Expr\Assign) {
            return [];
        }

        $resultType = $scope->getType($node);
        if (! FloatTypeHelper::isFloat($resultType)) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Cannot assign %s to %s - floats are not allowed.',
                $resultType->describe(VerbosityLevel::typeOnly()),
                $this->printer->prettyPrintExpr($node->var),
            ))->identifier('float.assign')->build(),
        ];
    }
}


================================================
FILE: src/DisallowFloatEverywhereRule.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStan\Rules\Floats;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\VerbosityLevel;

use function sprintf;

/**
 * @implements Rule<Expr>
 * @final not designed for inheritance, but kept open for BC, until the next major release
 */
class DisallowFloatEverywhereRule implements Rule
{
    public function getNodeType(): string
    {
        return Expr::class;
    }

    /**
     * {@inheritDoc}
     */
    public function processNode(Node $node, Scope $scope): array
    {
        if (
            $node instanceof Node\Expr\AssignOp
            || $node instanceof Node\Expr\Assign
        ) {
            return [];
        }

        $nodeType = $scope->getType($node);
        if (! FloatTypeHelper::isFloat($nodeType)) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Cannot have %s as a result type of this expression - floats are not allowed.',
                $nodeType->describe(VerbosityLevel::typeOnly()),
            ))->identifier('float.expression')->build(),
        ];
    }
}


================================================
FILE: src/DisallowFloatInFunctionSignatureRule.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStan\Rules\Floats;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Function_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\VerbosityLevel;

use function array_filter;
use function array_keys;
use function array_map;
use function array_merge;
use function array_values;
use function sprintf;

/** @implements Rule<Function_> */
final class DisallowFloatInFunctionSignatureRule implements Rule
{
    public function __construct(private ReflectionProvider $reflectionProvider)
    {
    }

    public function getNodeType(): string
    {
        return Function_::class;
    }

    /**
     * {@inheritDoc}
     */
    public function processNode(Node $node, Scope $scope): array
    {
        $functionName = new Name($node->name->toString());
        if (! $this->reflectionProvider->hasFunction($functionName, $scope)) {
            return [];
        }

        $functionReflection = $this->reflectionProvider->getFunction($functionName, $scope);

        $errors = [];

        foreach ($functionReflection->getVariants() as $functionVariant) {
            $errors[] = $this->violationsForParameters($functionVariant, $functionReflection);
            $errors[] = $this->returnTypeViolations($functionVariant, $functionReflection);
        }

        return array_merge([], ...$errors);
    }

    /** @return list<IdentifierRuleError> */
    private function returnTypeViolations(
        ParametersAcceptor $function,
        FunctionReflection $functionReflection,
    ): array {
        if (! FloatTypeHelper::isFloat($function->getReturnType())) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Function %s() cannot have %s as its return type - floats are not allowed.',
                $functionReflection->getName(),
                $function->getReturnType()->describe(VerbosityLevel::typeOnly()),
            ))->identifier('float.function')->build(),
        ];
    }

    /** @return list<IdentifierRuleError> */
    private function violationsForParameters(
        ParametersAcceptor $function,
        FunctionReflection $functionReflection,
    ): array {
        $parameters = $function->getParameters();

        return array_values(array_filter(array_map(
            static function (ParameterReflection $parameter, int $index) use ($functionReflection): RuleError|null {
                if (! FloatTypeHelper::isFloat($parameter->getType())) {
                    return null;
                }

                return RuleErrorBuilder::message(sprintf(
                    'Parameter #%d $%s of function %s() cannot have %s as its type - floats are not allowed.',
                    $index + 1,
                    $parameter->getName(),
                    $functionReflection->getName(),
                    $parameter->getType()->describe(VerbosityLevel::typeOnly()),
                ))->identifier('float.function')->build();
            },
            $parameters,
            array_keys($parameters),
        ), static fn (RuleError|null $error): bool => $error !== null));
    }
}


================================================
FILE: src/DisallowFloatInMethodSignatureRule.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStan\Rules\Floats;

use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\VerbosityLevel;

use function array_filter;
use function array_keys;
use function array_map;
use function array_merge;
use function array_values;
use function sprintf;

/** @implements Rule<ClassMethod> */
final class DisallowFloatInMethodSignatureRule implements Rule
{
    public function getNodeType(): string
    {
        return ClassMethod::class;
    }

    /**
     * {@inheritDoc}
     */
    public function processNode(Node $node, Scope $scope): array
    {
        if (! $scope->isInClass()) {
            throw new ShouldNotHappenException();
        }

        /** @psalm-var ClassReflection $classReflection */
        $classReflection = $scope->getClassReflection();
        $methodName      = $node->name->toString();
        $method          = $classReflection->getNativeMethod($methodName);

        $errors = [];

        foreach ($method->getVariants() as $methodVariant) {
            $errors[] = $this->violationsForParameters($methodVariant, $method);
            $errors[] = $this->returnTypeViolations($methodVariant, $method);
        }

        return array_merge([], ...$errors);
    }

    /** @return list<IdentifierRuleError> */
    private function returnTypeViolations(
        ParametersAcceptor $method,
        MethodReflection $methodReflection,
    ): array {
        if (! FloatTypeHelper::isFloat($method->getReturnType())) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Method %s::%s() cannot have %s as its return type - floats are not allowed.',
                $methodReflection->getDeclaringClass()->getDisplayName(),
                $methodReflection->getName(),
                $method->getReturnType()->describe(VerbosityLevel::typeOnly()),
            ))->identifier('float.type')->build(),
        ];
    }

    /** @return list<IdentifierRuleError> */
    private function violationsForParameters(
        ParametersAcceptor $function,
        MethodReflection $methodReflection,
    ): array {
        $parameters = $function->getParameters();

        return array_values(array_filter(array_map(
            static function (ParameterReflection $parameter, int $index) use ($methodReflection): RuleError|null {
                if (! FloatTypeHelper::isFloat($parameter->getType())) {
                    return null;
                }

                return RuleErrorBuilder::message(sprintf(
                    'Parameter #%d $%s of method %s::%s() cannot have %s as its type - floats are not allowed.',
                    $index + 1,
                    $parameter->getName(),
                    $methodReflection->getDeclaringClass()->getDisplayName(),
                    $methodReflection->getName(),
                    $parameter->getType()->describe(VerbosityLevel::typeOnly()),
                ))->identifier('float.type')->build();
            },
            $parameters,
            array_keys($parameters),
        ), static fn (RuleError|null $error): bool => $error !== null));
    }
}


================================================
FILE: src/DisallowFloatPropertyTypeRule.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStan\Rules\Floats;

use PhpParser\Node;
use PhpParser\Node\PropertyItem;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\VerbosityLevel;

use function sprintf;

/** @implements Rule<PropertyItem> */
final class DisallowFloatPropertyTypeRule implements Rule
{
    public function getNodeType(): string
    {
        return PropertyItem::class;
    }

    /**
     * {@inheritDoc}
     */
    public function processNode(Node $node, Scope $scope): array
    {
        if (! $scope->isInClass()) {
            throw new ShouldNotHappenException();
        }

        /** @psalm-var ClassReflection $classReflection */
        $classReflection = $scope->getClassReflection();
        $propertyName    = $node->name->toString();
        $property        = $classReflection->getNativeProperty($node->name->toString());
        $propertyType    = $property->getReadableType();
        if (! FloatTypeHelper::isFloat($propertyType)) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Property %s::$%s cannot have %s as its type - floats are not allowed.',
                $property->getDeclaringClass()->getDisplayName(),
                $propertyName,
                $propertyType->describe(VerbosityLevel::typeOnly()),
            ))->identifier('float.property')->build(),
        ];
    }
}


================================================
FILE: src/FloatTypeHelper.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStan\Rules\Floats;

use PHPStan\Type\FloatType;
use PHPStan\Type\LateResolvableType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;

/** @internal class is only for internal tooling use: do not import it in your own projects */
final class FloatTypeHelper
{
    public static function isFloat(Type $type): bool
    {
        if ($type instanceof MixedType) {
            return false;
        }

        if ($type instanceof NeverType) {
            return false;
        }

        if ($type instanceof LateResolvableType) {
            return self::isFloat($type->resolve());
        }

        return ! (new FloatType())->isSuperTypeOf($type)->no();
    }
}


================================================
FILE: tests/asset/assign.php
================================================
<?php

namespace DisallowFloatsInAssign;

class Foo
{

    private $foo;

    private $bar;

    /**
     * @param float $float
     * @param int|float $intOrFloat
     */
    public function doFoo($float, $intOrFloat, string $str)
    {
        $test = $float;
        $test2 = $intOrFloat;

        $this->foo = $float;
        $this->bar['test'] = $float;

        $test3 = $str;
    }

    public function doBar()
    {
        $test = 0;
        $test += 1;
        $test += 3.14;
    }

}


================================================
FILE: tests/asset/expr.php
================================================
<?php

namespace DisallowFloatsEverywhere;

function () {
    $foo = 1.3;
    $foo += 1.3;

    foo(
        3.14
    );
};


================================================
FILE: tests/asset/function.php
================================================
<?php

namespace DisallowFloatsInFunctionSignatures;

/**
 * @param float $float
 * @param int|float $intOrFloat
 * @param string $string
 * @return float
 */
function doFoo(
    float $float,
    $intOrFloat,
    string $string,
    $mixed
): float
{

}

function doBar(): string
{

}

/**
 * @return never
 */
function withNever()
{
    throw new \RuntimeException();
}


================================================
FILE: tests/asset/functionNotAutoloaded.php
================================================
<?php

function nonexistentRoaveFunction(): float
{

}


================================================
FILE: tests/asset/functionWithInterpolatedFloatAndNonFloatParameters.php
================================================
<?php

namespace DisallowFloatsInFunctionSignatures;

/** @param mixed $d */
function functionWithInterpolatedFloatAndNonFloatParameters(
    float $a,
    string $b,
    float $c,
    $d,
    float $e
) {

}


================================================
FILE: tests/asset/functionWithoutNamespace.php
================================================
<?php

/**
 * @param float $float
 * @param int|float $intOrFloat
 * @param string $string
 * @return float
 */
function doFoo(
    float $float,
    $intOrFloat,
    string $string,
    $mixed
): float
{

}

function doBar(): string
{

}


================================================
FILE: tests/asset/method.php
================================================
<?php

namespace DisallowFloatsInMethodSignatures;

class Foo
{

    /**
     * @param float $float
     * @param int|float $intOrFloat
     * @param string $string
     * @return float
     */
    public function doFoo(
        float $float,
        $intOrFloat,
        string $string,
        $mixed
    ): float
    {

    }

    public function doBar(): string
    {

    }

    /**
     * @return never
     */
    public function withNever()
    {
        throw new \RuntimeException();
    }
}


================================================
FILE: tests/asset/methodWithConditionalReturnType.php
================================================
<?php

namespace DisallowFloatsInMethodSignatures;

use Exception;

use function gettype;

/**
 * @template TObject of object
 */
interface DenormalizerInterface
{
    /**
     * @param class-string<TObject>|string $type
     * @return ($type is class-string<TObject> ? TObject : mixed)
     */
    public function denormalize(mixed $data, string $type): mixed;
}

class Denormalizer implements DenormalizerInterface
{
    public function denormalize(mixed $data, string $type): mixed
    {
        return new \stdClass();
    }
}

class DummyTypeConverter {
    /**
     * @template RequestedType of 'float'|'int'
     * @param RequestedType $type
     * @return (RequestedType is 'float' ? float : int)
     */
    function convertToNumber(mixed $input, string $type): mixed {
        throw new Exception('irrelevant - ' . gettype($input) . ' - ' . $type);
    }

    /**
     * @template RequestedType of 'float'|'int'
     * @param RequestedType $type
     * @return (RequestedType is 'int' ? int : float)
     */
    function convertToNumber2(mixed $input, string $type): mixed {
        throw new Exception('irrelevant - ' . gettype($input) . ' - ' . $type);
    }
}

================================================
FILE: tests/asset/methodWithInterpolatedFloatAndNotFloatParameters.php
================================================
<?php

namespace DisallowFloatsInMethodSignatures;

class Bar
{
    /** @param mixed $d */
    public function doFoo(
        float $a,
        string $b,
        float $c,
        $d,
        float $e
    ) {

    }
}


================================================
FILE: tests/asset/property.php
================================================
<?php

namespace DisallowFloatsInProperties;

class Foo
{

    /** @var float */
    private $foo;

    /** @var float|int */
    private $bar;

    /** @var int */
    private $baz;

    /** @var mixed */
    private $taz;

}


================================================
FILE: tests/src/DisallowFloatAssignedToVariableRuleTest.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStanTest\Rules\Floats;

use PHPStan\Node\Printer\Printer;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Roave\PHPStan\Rules\Floats\DisallowFloatAssignedToVariableRule;

/** @extends RuleTestCase<DisallowFloatAssignedToVariableRule> */
final class DisallowFloatAssignedToVariableRuleTest extends RuleTestCase
{
    protected function getRule(): Rule
    {
        /** @phpstan-ignore phpstanApi.constructor */
        return new DisallowFloatAssignedToVariableRule(new Printer());
    }

    public function testRule(): void
    {
        $this->analyse([__DIR__ . '/../asset/assign.php'], [
            [
                'Cannot assign float to $test - floats are not allowed.',
                18,
            ],
            [
                'Cannot assign float|int to $test2 - floats are not allowed.',
                19,
            ],
            [
                'Cannot assign float to $this->foo - floats are not allowed.',
                21,
            ],
            [
                'Cannot assign float to $this->bar[\'test\'] - floats are not allowed.',
                22,
            ],
            [
                'Cannot assign float to $test - floats are not allowed.',
                31,
            ],
        ]);
    }
}


================================================
FILE: tests/src/DisallowFloatEverywhereRuleTest.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStanTest\Rules\Floats;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Roave\PHPStan\Rules\Floats\DisallowFloatEverywhereRule;

/** @extends RuleTestCase<DisallowFloatEverywhereRule> */
final class DisallowFloatEverywhereRuleTest extends RuleTestCase
{
    protected function getRule(): Rule
    {
        return new DisallowFloatEverywhereRule();
    }

    public function testRule(): void
    {
        $this->analyse([__DIR__ . '/../asset/expr.php'], [
            [
                'Cannot have float as a result type of this expression - floats are not allowed.',
                6,
            ],
            [
                'Cannot have float as a result type of this expression - floats are not allowed.',
                7,
            ],
            [
                'Cannot have float as a result type of this expression - floats are not allowed.',
                7,
            ],
            [
                'Cannot have float as a result type of this expression - floats are not allowed.',
                10,
            ],
        ]);
    }
}


================================================
FILE: tests/src/DisallowFloatInFunctionSignatureRuleTest.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStanTest\Rules\Floats;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Roave\PHPStan\Rules\Floats\DisallowFloatInFunctionSignatureRule;

/** @extends RuleTestCase<DisallowFloatInFunctionSignatureRule> */
final class DisallowFloatInFunctionSignatureRuleTest extends RuleTestCase
{
    protected function getRule(): Rule
    {
        return new DisallowFloatInFunctionSignatureRule($this->createReflectionProvider());
    }

    public function testRule(): void
    {
        require_once __DIR__ . '/../asset/function.php';
        $this->analyse([__DIR__ . '/../asset/function.php'], [
            [
                'Parameter #1 $float of function DisallowFloatsInFunctionSignatures\doFoo() cannot have float as its type - floats are not allowed.',
                11,
            ],
            [
                'Parameter #2 $intOrFloat of function DisallowFloatsInFunctionSignatures\doFoo() cannot have float|int as its type - floats are not allowed.',
                11,
            ],
            [
                'Function DisallowFloatsInFunctionSignatures\doFoo() cannot have float as its return type - floats are not allowed.',
                11,
            ],
        ]);
    }

    public function testRuleWithoutNamespace(): void
    {
        require_once __DIR__ . '/../asset/functionWithoutNamespace.php';
        $this->analyse([__DIR__ . '/../asset/functionWithoutNamespace.php'], [
            [
                'Parameter #1 $float of function doFoo() cannot have float as its type - floats are not allowed.',
                9,
            ],
            [
                'Parameter #2 $intOrFloat of function doFoo() cannot have float|int as its type - floats are not allowed.',
                9,
            ],
            [
                'Function doFoo() cannot have float as its return type - floats are not allowed.',
                9,
            ],
        ]);
    }

    public function testRuleShowsAllFloatParametersAsViolations(): void
    {
        require_once __DIR__ . '/../asset/functionWithInterpolatedFloatAndNonFloatParameters.php';
        $this->analyse([__DIR__ . '/../asset/functionWithInterpolatedFloatAndNonFloatParameters.php'], [
            [
                'Parameter #1 $a of function DisallowFloatsInFunctionSignatures\functionWithInterpolatedFloatAndNonFloatParameters() cannot have float as its type - floats are not allowed.',
                6,
            ],
            [
                'Parameter #3 $c of function DisallowFloatsInFunctionSignatures\functionWithInterpolatedFloatAndNonFloatParameters() cannot have float as its type - floats are not allowed.',
                6,
            ],
            [
                'Parameter #5 $e of function DisallowFloatsInFunctionSignatures\functionWithInterpolatedFloatAndNonFloatParameters() cannot have float as its type - floats are not allowed.',
                6,
            ],
        ]);
    }

    public function testNotAutoloadedFunction(): void
    {
        $this->analyse(
            [__DIR__ . '/../asset/functionNotAutoloaded.php'],
            [
                [
                    'Function nonexistentRoaveFunction() cannot have float as its return type - floats are not allowed.',
                    3,
                ],
            ],
        );
    }
}


================================================
FILE: tests/src/DisallowFloatInMethodSignatureRuleTest.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStanTest\Rules\Floats;

use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Rules\Rule;
use PHPStan\ShouldNotHappenException;
use PHPStan\Testing\RuleTestCase;
use Roave\PHPStan\Rules\Floats\DisallowFloatInMethodSignatureRule;

/** @extends RuleTestCase<DisallowFloatInMethodSignatureRule> */
final class DisallowFloatInMethodSignatureRuleTest extends RuleTestCase
{
    protected function getRule(): Rule
    {
        return new DisallowFloatInMethodSignatureRule();
    }

    public function testRule(): void
    {
        $this->analyse([__DIR__ . '/../asset/method.php'], [
            [
                'Parameter #1 $float of method DisallowFloatsInMethodSignatures\Foo::doFoo() cannot have float as its type - floats are not allowed.',
                14,
            ],
            [
                'Parameter #2 $intOrFloat of method DisallowFloatsInMethodSignatures\Foo::doFoo() cannot have float|int as its type - floats are not allowed.',
                14,
            ],
            [
                'Method DisallowFloatsInMethodSignatures\Foo::doFoo() cannot have float as its return type - floats are not allowed.',
                14,
            ],
        ]);
    }

    public function testRuleShowsAllFloatParametersAsViolations(): void
    {
        $this->analyse([__DIR__ . '/../asset/methodWithInterpolatedFloatAndNotFloatParameters.php'], [
            [
                'Parameter #1 $a of method DisallowFloatsInMethodSignatures\Bar::doFoo() cannot have float as its type - floats are not allowed.',
                8,
            ],
            [
                'Parameter #3 $c of method DisallowFloatsInMethodSignatures\Bar::doFoo() cannot have float as its type - floats are not allowed.',
                8,
            ],
            [
                'Parameter #5 $e of method DisallowFloatsInMethodSignatures\Bar::doFoo() cannot have float as its type - floats are not allowed.',
                8,
            ],
        ]);
    }

    /**
     * Verifies that conditional return types containing mixed are not flagged as floats.
     *
     * @see https://github.com/Roave/no-floaters/issues/126
     */
    public function testRuleDoesNotFlagConditionalReturnTypesContainingMixed(): void
    {
        $this->analyse(
            [__DIR__ . '/../asset/methodWithConditionalReturnType.php'],
            [
                [
                    'Method DisallowFloatsInMethodSignatures\DummyTypeConverter::convertToNumber() cannot have (RequestedType of string is string ? float : int) as its return type - floats are not allowed.',
                    35,
                ],
                [
                    'Method DisallowFloatsInMethodSignatures\DummyTypeConverter::convertToNumber2() cannot have (RequestedType of string is string ? int : float) as its return type - floats are not allowed.',
                    44,
                ],
            ],
        );
    }

    /**
     * Verifies that the impossible scenario of a method signature is not declared in a class method
     */
    public function testRuleWillNotWorkWhenNotInClassScope(): void
    {
        $rule = new DisallowFloatInMethodSignatureRule();

        $node  = self::createStub(ClassMethod::class);
        $scope = self::createStub(ScopeWithNodeCallbackInvoker::class);

        $scope
            ->method('isInClass')
            ->willReturn(false);

        $this->expectException(ShouldNotHappenException::class);
        $this->expectExceptionMessage('Internal error.');

        $rule->processNode($node, $scope);
    }
}


================================================
FILE: tests/src/DisallowFloatPropertyTypeRuleTest.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStanTest\Rules\Floats;

use PhpParser\Node\PropertyItem;
use PHPStan\Rules\Rule;
use PHPStan\ShouldNotHappenException;
use PHPStan\Testing\RuleTestCase;
use Roave\PHPStan\Rules\Floats\DisallowFloatPropertyTypeRule;

/** @extends RuleTestCase<DisallowFloatPropertyTypeRule> */
final class DisallowFloatPropertyTypeRuleTest extends RuleTestCase
{
    protected function getRule(): Rule
    {
        return new DisallowFloatPropertyTypeRule();
    }

    public function testRule(): void
    {
        $this->analyse([__DIR__ . '/../asset/property.php'], [
            [
                'Property DisallowFloatsInProperties\Foo::$foo cannot have float as its type - floats are not allowed.',
                9,
            ],
            [
                'Property DisallowFloatsInProperties\Foo::$bar cannot have float|int as its type - floats are not allowed.',
                12,
            ],
        ]);
    }

    /**
     * Verifies that the impossible scenario of a method signature is not declared in a class method
     */
    public function testRuleWillNotWorkWhenNotInClassScope(): void
    {
        $rule = new DisallowFloatPropertyTypeRule();

        $node  = self::createStub(PropertyItem::class);
        $scope = self::createStub(ScopeWithNodeCallbackInvoker::class);

        $scope
            ->method('isInClass')
            ->willReturn(false);

        $this->expectException(ShouldNotHappenException::class);
        $this->expectExceptionMessage('Internal error.');

        $rule->processNode($node, $scope);
    }
}


================================================
FILE: tests/src/ScopeWithNodeCallbackInvoker.php
================================================
<?php

declare(strict_types=1);

namespace Roave\PHPStanTest\Rules\Floats;

use PHPStan\Analyser\NodeCallbackInvoker;
use PHPStan\Analyser\Scope;

/** @phpstan-ignore phpstanApi.interface */
interface ScopeWithNodeCallbackInvoker extends Scope, NodeCallbackInvoker
{
}
Download .txt
gitextract_9qceq59d/

├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── continuous-integration.yml
│       └── release-on-milestone-closed.yml
├── .gitignore
├── .laminas-ci.json
├── LICENSE
├── README.md
├── SECURITY.md
├── composer-require-checker.json
├── composer.json
├── infection.json.dist
├── logo/
│   └── roave-no-floaters.blend
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── psalm.xml
├── renovate.json
├── rules.neon
├── src/
│   ├── DisallowFloatAssignedToVariableRule.php
│   ├── DisallowFloatEverywhereRule.php
│   ├── DisallowFloatInFunctionSignatureRule.php
│   ├── DisallowFloatInMethodSignatureRule.php
│   ├── DisallowFloatPropertyTypeRule.php
│   └── FloatTypeHelper.php
└── tests/
    ├── asset/
    │   ├── assign.php
    │   ├── expr.php
    │   ├── function.php
    │   ├── functionNotAutoloaded.php
    │   ├── functionWithInterpolatedFloatAndNonFloatParameters.php
    │   ├── functionWithoutNamespace.php
    │   ├── method.php
    │   ├── methodWithConditionalReturnType.php
    │   ├── methodWithInterpolatedFloatAndNotFloatParameters.php
    │   └── property.php
    └── src/
        ├── DisallowFloatAssignedToVariableRuleTest.php
        ├── DisallowFloatEverywhereRuleTest.php
        ├── DisallowFloatInFunctionSignatureRuleTest.php
        ├── DisallowFloatInMethodSignatureRuleTest.php
        ├── DisallowFloatPropertyTypeRuleTest.php
        └── ScopeWithNodeCallbackInvoker.php
Download .txt
SYMBOL INDEX (70 symbols across 21 files)

FILE: src/DisallowFloatAssignedToVariableRule.php
  class DisallowFloatAssignedToVariableRule (line 17) | final class DisallowFloatAssignedToVariableRule implements Rule
    method __construct (line 19) | public function __construct(private Printer $printer)
    method getNodeType (line 23) | public function getNodeType(): string
    method processNode (line 31) | public function processNode(Node $node, Scope $scope): array

FILE: src/DisallowFloatEverywhereRule.php
  class DisallowFloatEverywhereRule (line 20) | class DisallowFloatEverywhereRule implements Rule
    method getNodeType (line 22) | public function getNodeType(): string
    method processNode (line 30) | public function processNode(Node $node, Scope $scope): array

FILE: src/DisallowFloatInFunctionSignatureRule.php
  class DisallowFloatInFunctionSignatureRule (line 29) | final class DisallowFloatInFunctionSignatureRule implements Rule
    method __construct (line 31) | public function __construct(private ReflectionProvider $reflectionProv...
    method getNodeType (line 35) | public function getNodeType(): string
    method processNode (line 43) | public function processNode(Node $node, Scope $scope): array
    method returnTypeViolations (line 63) | private function returnTypeViolations(
    method violationsForParameters (line 81) | private function violationsForParameters(

FILE: src/DisallowFloatInMethodSignatureRule.php
  class DisallowFloatInMethodSignatureRule (line 29) | final class DisallowFloatInMethodSignatureRule implements Rule
    method getNodeType (line 31) | public function getNodeType(): string
    method processNode (line 39) | public function processNode(Node $node, Scope $scope): array
    method returnTypeViolations (line 61) | private function returnTypeViolations(
    method violationsForParameters (line 80) | private function violationsForParameters(

FILE: src/DisallowFloatPropertyTypeRule.php
  class DisallowFloatPropertyTypeRule (line 19) | final class DisallowFloatPropertyTypeRule implements Rule
    method getNodeType (line 21) | public function getNodeType(): string
    method processNode (line 29) | public function processNode(Node $node, Scope $scope): array

FILE: src/FloatTypeHelper.php
  class FloatTypeHelper (line 14) | final class FloatTypeHelper
    method isFloat (line 16) | public static function isFloat(Type $type): bool

FILE: tests/asset/assign.php
  class Foo (line 5) | class Foo
    method doFoo (line 16) | public function doFoo($float, $intOrFloat, string $str)
    method doBar (line 27) | public function doBar()

FILE: tests/asset/function.php
  function doFoo (line 11) | function doFoo(
  function doBar (line 21) | function doBar(): string
  function withNever (line 29) | function withNever()

FILE: tests/asset/functionNotAutoloaded.php
  function nonexistentRoaveFunction (line 3) | function nonexistentRoaveFunction(): float

FILE: tests/asset/functionWithInterpolatedFloatAndNonFloatParameters.php
  function functionWithInterpolatedFloatAndNonFloatParameters (line 6) | function functionWithInterpolatedFloatAndNonFloatParameters(

FILE: tests/asset/functionWithoutNamespace.php
  function doFoo (line 9) | function doFoo(
  function doBar (line 19) | function doBar(): string

FILE: tests/asset/method.php
  class Foo (line 5) | class Foo
    method doFoo (line 14) | public function doFoo(
    method doBar (line 24) | public function doBar(): string
    method withNever (line 32) | public function withNever()

FILE: tests/asset/methodWithConditionalReturnType.php
  type DenormalizerInterface (line 12) | interface DenormalizerInterface
    method denormalize (line 18) | public function denormalize(mixed $data, string $type): mixed;
  class Denormalizer (line 21) | class Denormalizer implements DenormalizerInterface
    method denormalize (line 23) | public function denormalize(mixed $data, string $type): mixed
  class DummyTypeConverter (line 29) | class DummyTypeConverter {
    method convertToNumber (line 35) | function convertToNumber(mixed $input, string $type): mixed {
    method convertToNumber2 (line 44) | function convertToNumber2(mixed $input, string $type): mixed {

FILE: tests/asset/methodWithInterpolatedFloatAndNotFloatParameters.php
  class Bar (line 5) | class Bar
    method doFoo (line 8) | public function doFoo(

FILE: tests/asset/property.php
  class Foo (line 5) | class Foo

FILE: tests/src/DisallowFloatAssignedToVariableRuleTest.php
  class DisallowFloatAssignedToVariableRuleTest (line 13) | final class DisallowFloatAssignedToVariableRuleTest extends RuleTestCase
    method getRule (line 15) | protected function getRule(): Rule
    method testRule (line 21) | public function testRule(): void

FILE: tests/src/DisallowFloatEverywhereRuleTest.php
  class DisallowFloatEverywhereRuleTest (line 12) | final class DisallowFloatEverywhereRuleTest extends RuleTestCase
    method getRule (line 14) | protected function getRule(): Rule
    method testRule (line 19) | public function testRule(): void

FILE: tests/src/DisallowFloatInFunctionSignatureRuleTest.php
  class DisallowFloatInFunctionSignatureRuleTest (line 12) | final class DisallowFloatInFunctionSignatureRuleTest extends RuleTestCase
    method getRule (line 14) | protected function getRule(): Rule
    method testRule (line 19) | public function testRule(): void
    method testRuleWithoutNamespace (line 38) | public function testRuleWithoutNamespace(): void
    method testRuleShowsAllFloatParametersAsViolations (line 57) | public function testRuleShowsAllFloatParametersAsViolations(): void
    method testNotAutoloadedFunction (line 76) | public function testNotAutoloadedFunction(): void

FILE: tests/src/DisallowFloatInMethodSignatureRuleTest.php
  class DisallowFloatInMethodSignatureRuleTest (line 14) | final class DisallowFloatInMethodSignatureRuleTest extends RuleTestCase
    method getRule (line 16) | protected function getRule(): Rule
    method testRule (line 21) | public function testRule(): void
    method testRuleShowsAllFloatParametersAsViolations (line 39) | public function testRuleShowsAllFloatParametersAsViolations(): void
    method testRuleDoesNotFlagConditionalReturnTypesContainingMixed (line 62) | public function testRuleDoesNotFlagConditionalReturnTypesContainingMix...
    method testRuleWillNotWorkWhenNotInClassScope (line 82) | public function testRuleWillNotWorkWhenNotInClassScope(): void

FILE: tests/src/DisallowFloatPropertyTypeRuleTest.php
  class DisallowFloatPropertyTypeRuleTest (line 14) | final class DisallowFloatPropertyTypeRuleTest extends RuleTestCase
    method getRule (line 16) | protected function getRule(): Rule
    method testRule (line 21) | public function testRule(): void
    method testRuleWillNotWorkWhenNotInClassScope (line 38) | public function testRuleWillNotWorkWhenNotInClassScope(): void

FILE: tests/src/ScopeWithNodeCallbackInvoker.php
  type ScopeWithNodeCallbackInvoker (line 11) | interface ScopeWithNodeCallbackInvoker extends Scope, NodeCallbackInvoker
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (46K chars).
[
  {
    "path": ".gitattributes",
    "chars": 283,
    "preview": "logo export-ignore\ntests export-ignore\n.gitignore export-ignore\n.gitattributes export-ignore\n.travis.yml export-ignore\nc"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 40,
    "preview": "tidelift: \"packagist/roave/no-floaters\"\n"
  },
  {
    "path": ".github/workflows/continuous-integration.yml",
    "chars": 1078,
    "preview": "# See https://github.com/laminas/laminas-continuous-integration-action\n# Generates a job matrix based on current depende"
  },
  {
    "path": ".github/workflows/release-on-milestone-closed.yml",
    "chars": 2525,
    "preview": "# https://help.github.com/en/categories/automating-your-workflow-with-github-actions\n\nname: \"Automatic Releases\"\n\non:\n  "
  },
  {
    "path": ".gitignore",
    "chars": 44,
    "preview": "/vendor\n.phpunit.cache\n.phpunit.result.cache"
  },
  {
    "path": ".laminas-ci.json",
    "chars": 314,
    "preview": "{\n  \"extensions\": [\n    \"pcov\"\n  ],\n  \"exclude\": [\n    {\"name\": \"Infection\"}\n  ],\n  \"additional_checks\": [\n    {\n      \""
  },
  {
    "path": "LICENSE",
    "chars": 1062,
    "preview": "MIT License\n\nCopyright (c) Roave, LLC\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
  },
  {
    "path": "README.md",
    "chars": 2912,
    "preview": "# roave/no-floaters\n\n![`roave/no-floaters`](logo/roave-no-floaters.png)\n\n[![Latest Stable Version](https://poser.pugx.or"
  },
  {
    "path": "SECURITY.md",
    "chars": 193,
    "preview": "## Security contact information\n\nTo report a security vulnerability, please use the\n[Tidelift security contact](https://"
  },
  {
    "path": "composer-require-checker.json",
    "chars": 1080,
    "preview": "{\n    \"symbol-whitelist\": [\n        \"null\",\n        \"true\",\n        \"false\",\n        \"static\",\n        \"self\",\n        \""
  },
  {
    "path": "composer.json",
    "chars": 1390,
    "preview": "{\n    \"name\": \"roave/no-floaters\",\n    \"type\": \"phpstan-extension\",\n    \"description\": \"PHPStan Rules to Disallow Float "
  },
  {
    "path": "infection.json.dist",
    "chars": 316,
    "preview": "{\n    \"$schema\": \"vendor/infection/infection/resources/schema.json\",\n    \"source\": {\n        \"directories\": [\n          "
  },
  {
    "path": "phpcs.xml.dist",
    "chars": 367,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset\n    name=\"PHPStan Disallow Float Rules\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-i"
  },
  {
    "path": "phpstan.neon.dist",
    "chars": 274,
    "preview": "includes:\n\t- vendor/phpstan/phpstan-phpunit/extension.neon\n\t- vendor/phpstan/phpstan-phpunit/rules.neon\n\t- vendor/phpsta"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 757,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xs"
  },
  {
    "path": "psalm.xml",
    "chars": 1225,
    "preview": "<?xml version=\"1.0\"?>\n<psalm\n    errorLevel=\"1\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns=\"htt"
  },
  {
    "path": "renovate.json",
    "chars": 134,
    "preview": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"local>Ocramius/.github:renovate-"
  },
  {
    "path": "rules.neon",
    "chars": 566,
    "preview": "rules:\n\t- Roave\\PHPStan\\Rules\\Floats\\DisallowFloatAssignedToVariableRule\n\t- Roave\\PHPStan\\Rules\\Floats\\DisallowFloatInFu"
  },
  {
    "path": "src/DisallowFloatAssignedToVariableRule.php",
    "chars": 1222,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStan\\Rules\\Floats;\n\nuse PhpParser\\Node;\nuse PHPStan\\Analyser\\Scope;\n"
  },
  {
    "path": "src/DisallowFloatEverywhereRule.php",
    "chars": 1218,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStan\\Rules\\Floats;\n\nuse PhpParser\\Node;\nuse PhpParser\\Node\\Expr;\nuse"
  },
  {
    "path": "src/DisallowFloatInFunctionSignatureRule.php",
    "chars": 3455,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStan\\Rules\\Floats;\n\nuse PhpParser\\Node;\nuse PhpParser\\Node\\Name;\nuse"
  },
  {
    "path": "src/DisallowFloatInMethodSignatureRule.php",
    "chars": 3536,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStan\\Rules\\Floats;\n\nuse PhpParser\\Node;\nuse PhpParser\\Node\\Stmt\\Clas"
  },
  {
    "path": "src/DisallowFloatPropertyTypeRule.php",
    "chars": 1541,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStan\\Rules\\Floats;\n\nuse PhpParser\\Node;\nuse PhpParser\\Node\\PropertyI"
  },
  {
    "path": "src/FloatTypeHelper.php",
    "chars": 745,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStan\\Rules\\Floats;\n\nuse PHPStan\\Type\\FloatType;\nuse PHPStan\\Type\\Lat"
  },
  {
    "path": "tests/asset/assign.php",
    "chars": 495,
    "preview": "<?php\n\nnamespace DisallowFloatsInAssign;\n\nclass Foo\n{\n\n    private $foo;\n\n    private $bar;\n\n    /**\n     * @param float"
  },
  {
    "path": "tests/asset/expr.php",
    "chars": 124,
    "preview": "<?php\n\nnamespace DisallowFloatsEverywhere;\n\nfunction () {\n    $foo = 1.3;\n    $foo += 1.3;\n\n    foo(\n        3.14\n    );"
  },
  {
    "path": "tests/asset/function.php",
    "chars": 372,
    "preview": "<?php\n\nnamespace DisallowFloatsInFunctionSignatures;\n\n/**\n * @param float $float\n * @param int|float $intOrFloat\n * @par"
  },
  {
    "path": "tests/asset/functionNotAutoloaded.php",
    "chars": 55,
    "preview": "<?php\n\nfunction nonexistentRoaveFunction(): float\n{\n\n}\n"
  },
  {
    "path": "tests/asset/functionWithInterpolatedFloatAndNonFloatParameters.php",
    "chars": 209,
    "preview": "<?php\n\nnamespace DisallowFloatsInFunctionSignatures;\n\n/** @param mixed $d */\nfunction functionWithInterpolatedFloatAndNo"
  },
  {
    "path": "tests/asset/functionWithoutNamespace.php",
    "chars": 239,
    "preview": "<?php\n\n/**\n * @param float $float\n * @param int|float $intOrFloat\n * @param string $string\n * @return float\n */\nfunction"
  },
  {
    "path": "tests/asset/method.php",
    "chars": 502,
    "preview": "<?php\n\nnamespace DisallowFloatsInMethodSignatures;\n\nclass Foo\n{\n\n    /**\n     * @param float $float\n     * @param int|fl"
  },
  {
    "path": "tests/asset/methodWithConditionalReturnType.php",
    "chars": 1172,
    "preview": "<?php\n\nnamespace DisallowFloatsInMethodSignatures;\n\nuse Exception;\n\nuse function gettype;\n\n/**\n * @template TObject of o"
  },
  {
    "path": "tests/asset/methodWithInterpolatedFloatAndNotFloatParameters.php",
    "chars": 219,
    "preview": "<?php\n\nnamespace DisallowFloatsInMethodSignatures;\n\nclass Bar\n{\n    /** @param mixed $d */\n    public function doFoo(\n  "
  },
  {
    "path": "tests/asset/property.php",
    "chars": 227,
    "preview": "<?php\n\nnamespace DisallowFloatsInProperties;\n\nclass Foo\n{\n\n    /** @var float */\n    private $foo;\n\n    /** @var float|i"
  },
  {
    "path": "tests/src/DisallowFloatAssignedToVariableRuleTest.php",
    "chars": 1319,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStanTest\\Rules\\Floats;\n\nuse PHPStan\\Node\\Printer\\Printer;\nuse PHPSta"
  },
  {
    "path": "tests/src/DisallowFloatEverywhereRuleTest.php",
    "chars": 1133,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStanTest\\Rules\\Floats;\n\nuse PHPStan\\Rules\\Rule;\nuse PHPStan\\Testing\\"
  },
  {
    "path": "tests/src/DisallowFloatInFunctionSignatureRuleTest.php",
    "chars": 3370,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStanTest\\Rules\\Floats;\n\nuse PHPStan\\Rules\\Rule;\nuse PHPStan\\Testing\\"
  },
  {
    "path": "tests/src/DisallowFloatInMethodSignatureRuleTest.php",
    "chars": 3608,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStanTest\\Rules\\Floats;\n\nuse PhpParser\\Node\\Stmt\\ClassMethod;\nuse PHP"
  },
  {
    "path": "tests/src/DisallowFloatPropertyTypeRuleTest.php",
    "chars": 1600,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStanTest\\Rules\\Floats;\n\nuse PhpParser\\Node\\PropertyItem;\nuse PHPStan"
  },
  {
    "path": "tests/src/ScopeWithNodeCallbackInvoker.php",
    "chars": 269,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Roave\\PHPStanTest\\Rules\\Floats;\n\nuse PHPStan\\Analyser\\NodeCallbackInvoker;\nus"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the Roave/no-floaters GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (40.2 KB), approximately 10.9k tokens, and a symbol index with 70 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!