Git Product home page Git Product logo

psalm's Introduction

Psalm

Packagist Packagist Psalm coverage Psalm level

Psalm is a static analysis tool for finding errors in PHP applications.

Installation

To get started, check out the installation guide.

Live Demo

You can play around with Psalm on its website.

Documentation

Documentation is available on Psalm’s website, generated from the docs folder.

Interested in contributing?

Have a look at CONTRIBUTING.md.

Who made this

Built by Matt Brown (@muglug).

Maintained by Orklah (@orklah), Daniil Gentili (@danog), and Bruce Weirdan (@weirdan).

The engineering team at Vimeo have provided a lot encouragement, especially @nbeliard, @erunion and @nickyr.

psalm's People

Contributors

alies-dev avatar androlgenhald avatar bdsl avatar benmorel avatar boesing avatar bugreportuser avatar danog avatar edsrzf avatar fluffycondor avatar hirokinoue avatar issidorov avatar jack-worman avatar kamil-tekiela avatar kkmuffme avatar klimick avatar muglug avatar ocramius avatar ohader avatar orklah avatar othercorey avatar robchett avatar sammousa avatar signpostmarv avatar staabm avatar thbley avatar tm1000 avatar tysonandre avatar vincentlanglet avatar weirdan avatar ygottschalk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

psalm's Issues

Allow negated assignment in else

TypeTest::testNegatedAssignmentInIf():

if (!($row = (rand(0, 10) ? [5] : null))) {
    // do nothing
}  else {
    echo $row[0];
}

This code emits a MixedArrayOffset issue because the type the if condition evaluates to empty, and negating empty produces mixed.

MissingClosureReturnType issues with @return on container element

I have the following closure, and can't seem to get Psalm to recognize it having a return type:

$container['filesystem'] = function (Container $c) {
    $adapter = new Local('/');

    return new \League\Flysystem\Filesystem($adapter);
};

I've tried adding a /** @return \League\Flysystem\Filesystem */ above it, but it's still throwing an error.

ERROR: MissingReturnType - src/Provider/Filesystem.php:18 - Method  does not have a return type
        $container['filesystem'] = function (Container $c) {

Allow negated assignment

Currently fails:

if (($row = rand(0,10) ? [] : false) !== false) {
   $row[0] = 'good';
   echo $row[0];
}

Add support for aliased types

/** @psalm-type CoolType = A|B|null **/

...

/** @return CoolType */
function foo() {
...
}

/** @param CoolType $a **/
function bar ($a) : void { }

bar(foo());

Check types of operators

Languages like Java don't allow mismatched types on either side of an operator. After discussing offline with @muglug, we think that to keep Psalm from being too obnoxious, there may need to be two different levels of this check.

Looser level:
'the number is ' + 12 is illegal
'the number is ' . 12 is LEGAL
1.5 + 2 is LEGAL

Stricter level:
'the number is ' + 12 is illegal
'the number is ' . 12 is illegal
1.5 + 2 is illegal
1.0 + 2.0 is LEGAL

The looser level could be in the default config.

An example of where the stricter level can catch a bug: Say you were adding money and you had $price1 = 3.5 and $price2_cents = 50. Throwing an error if you did $price1 + $price2_cents would actually catch the bug since you shouldn’t be adding dollars and cents.

Psalm does not currently support varying levels of the same check, so the syntax for that in the XML will need to be determined.

Allow different errorLevels on different files

In the psalm.xml, you can currently set an error level for all files except a few, where it will be suppressed:

        <MissingReturnType errorLevel="info">
            <excludeFiles>
                <directory name="tests" />
            </excludeFiles>
        </MissingReturnType>

And you can set an error level for specific files, and suppress it everywhere else:

        <MissingReturnType errorLevel="info">
            <includeFiles>
                <directory name="src" />
            </includeFiles>
        </MissingReturnType>

But there's no way to have some files set to info, and some set to error. One possible syntax could be:

        <MissingReturnType errorLevel="info" />

        <!-- overrides the previous errorLevel for these files -->
        <MissingReturnType errorLevel="error">
            <includeFiles>
                <directory name="src" />
            </includeFiles>
        </MissingReturnType>

A big plus of this syntax is that it's backwards compatible with the current syntax. Downside is you now have rules for one error in two different blocks.

You could also add a tag like this, to avoid having two blocks and still be backwards compatible:

        <MissingReturnType errorLevel="info">
            <levelOverride errorLevel="error">
                <directory name="src" />
            </levelOverride>
        </MissingReturnType>

In this example, everything would get level 'info' except things in src would get level 'error'.

Open to any other suggestions too.

Closure param types not checked

$closure_int = function(int $i) : int { return $i; };
$closure_string = function(string $s) : string { return $s; };

print $closure_int("string") . "\n";
print $closure_string(42) . "\n";

Old style SoapClient constructor can't be extended

Support for old style constructors was added in 0c6d07e

However, if you add this to the bottom of the test in https://github.com/vimeo/psalm/blob/master/tests/Php40Test.php :

class C extends SoapClient {
    public function __construct() {
         parent::__construct();
    }
}

it fails with InaccessibleMethod - somefile.php:19 - Cannot access method SoapClient::__construct.

Poked around in the code a bit and it looks like since Psalm doesn't check SoapClient it doesn't know to register the constructor as __construct.

Add support for parameterised variable checks

This code passes (if $a is an array<mixed> and Foo::bar() is a method)

if ($a['b'] instanceof Foo) {
  $a['b']->bar();
}

but this code (assuming $b is defined) emits a MixedMethodCall issue:

if ($a[$b] instanceof Foo) {
  $a[$b]->bar();
}

It currently fails because Psalm does not allow array fetches with variable-key-offsets to be considered scoped variables. But we should adapt Psalm to allow variable-key array offsets, and then remove those variables from Context::$vars_in_scope when either the array is modified or the key variable is modified.

Related (slightly) to #11

Prevent improper composition

With Config::$use_property_default_for_type = true this emits an issue

<?php

class C1 {
    public $p = 42;
}

trait T1 {
    public $p = 'string';
}

class C2 extends C1 {
    use T1;
}

(borrowed from phan/phan#300)

InvalidPropertyAssignment - file.php:8 - $this->p with declared type 'int' cannot be assigned type 'string'

but should emit a stronger issue InvalidPropertyComposition

Undefined variable on assignment inside isset

<?php

$array = [];

if (isset($array[$a = microtime()])) {
    print "hello";
}

print $a;

Results in the following error, even if $a is always set here.

ERROR: UndefinedVariable - src/test.php:9 - Cannot find referenced variable $a
print $a;

Emit NotSetInConstructor issue

Given code

class A { }
class B {
    /** @var A **/
    public $a;

    public function __construct() {
    }
}

Where property B::$a cannot be null by definition, add an error if that property is not set at the end of the constructor.

Psalm thinks ReflectionMethod::invoke requires two parameters

Psalm thinks ReflectionMethod::invoke requires two parameters, however the second one is optional. You have to specify the second one as null to get it to pass.

Originally I opened this on the private repo, and you said it got fixed "when we started respecting the CallMap for method calls". But it still seems to be an issue on Psalm 0.3.3

Fix awkward instanceof && not null property

Given

<?php

class A {
  public function hello() {
    return 'hello';
  }
}

class B {
}

class C extends B {
  /** @var A|null */
  public $foo;
}

$maybe_c = rand(0, 10) ? new C() : new B();

,

if (!$maybe_c instanceof C || !$maybe_c->foo) {
  throw new \Exception('OK');
}

echo $maybe_c->foo->hello(); 

fails but

if (!$maybe_c instanceof C) {
  throw new \Exception('OK');
}

if (!$maybe_c->foo) {
  throw new \Exception('OK');
}

echo $maybe_c->foo->hello(); 

passes (even though they're functionally the same

Add support for contingent variables

This (bad) code currently emits a PossiblyUndefinedVariable issue. It shouldn't, as Psalm should know that $a is defined whenever $do_thing is !empty.

$do_thing = rand(0, 10) > 5;
if ($do_thing) {
    $a = 1;
}
if ($do_thing) {
    $b = $a;
}

This code should still emit a PossiblyUndefinedVariable issue, because $do_thing is redefined.

$do_thing = rand(0, 10) > 5;
if ($do_thing) {
    $a = 1;
}
$do_thing = rand(0, 10) > 5;
if ($do_thing) {
    $b = $a;
}

Including and excluding parts of the same directory doesn't work

I'm running the following config, and it doesn't appear to be properly excluding my tests/Stubs directory, because I have tests/ set up for inspection.

<?xml version="1.0"?>
<codeinspector stopOnFirstError="false" useDocblockTypes="true">
    <inspectFiles>
        <directory name="src" />
        <directory name="tests" />
    </inspectFiles>

    <excludeFiles>
        <directory name="tests/Stubs" />
    </excludeFiles>

    <fileExtensions>
        <extension name=".php" />
    </fileExtensions>
</codeinspector>
ERROR: MissingReturnType - tests/Stubs/RepresentationWithTwoFieldsButOnlyOneDocumentedStub.php:9 - Method Vimeo\Docparser\Tests\Stubs\RepresentationWithTwoFieldsButOnlyOneDocumentedStub::main does not have a return type
    protected function main()

Class constants not properly hoisted

This is fine

class A { const B = 42;}
$a = A::B;

But this produces an error

class A { const B = 42;}
$a = A::B;
class C {}

as we try to evaluate A::B before the class constants are hoisted.

Instead, Psalm should wait until it sees A::B before evaluating the class contents.

TypeDoesNotContainType issues with is_numeric() and strings

I have the following code that's parsing annotations for error codes, by looking for (\SomeError\Class::CASE) or (1337). Psalm seems to be having issues with the is_numeric check I'm doing to verify if the found error code is a numeric, or a FQN class name.

if (preg_match(self::REGEX_ERROR_CODE, $doc, $matches)) {
    $error_code = substr($matches[1], 1, -1);
    if (is_numeric($error_code)) {
        $parsed['error_code'] = $error_code;
    } else {
        if (!defined($error_code)) {
            throw UncallableErrorCodeException::create($this->docblock, $this->controller, $this->method);
        }

        $parsed['error_code'] = constant($error_code);
    }

    $doc = trim(preg_replace(self::REGEX_ERROR_CODE, '', $doc));
}
$ ./vendor/bin/psalm 
ERROR: TypeDoesNotContainType - src/Parser/Annotations/ThrowsAnnotation.php:116 - Cannot resolve types for $error_code - string does not contain numeric
            if (is_numeric($error_code)) {

Ideas?

Support a little algebra

Not currently supported by Psalm (or Hack, or Flow)

function do_algebra() : string {
    $a = rand(0, 10) > 5 ? "a" : null;
    $b = rand(0, 10) > 5 ? "b" : null;

    if ($a === null && $b === null) {
        return "all null";
    } elseif ($a === null) {
        return $b;
    } else {
        return $a;
    }
}

Add support for enums as parameters or return types

You always have to fake enums in PHP since they're not supported. Psalm could help this!

One option would be to do a syntax like this:

/**
 * @return 'A'|'B'|'C'
 */

The quotes would differentiate the strings from classes, and this would fit pretty naturally with the way Psalm currently allows notation with false like

/**
 * @return string|false
 */

Or, you could do something like

/**
 * @return enum{'A', 'B', 'C'}
 */

which would kind of match the current fancy array syntax.

Constants inside functions are not recognised

Test case:

/**
 * @return void
 */
function defineConstant()
{
    define('CONSTANT', 1);
}

defineConstant();

echo CONSTANT;

Output:

ERROR: UndefinedConstant - test-constant.php:13 - Const CONSTANT is not defined
echo CONSTANT;

Constants are always global, so there is no error in the above script.

Logical issue with instanceof subtraction

class Foo {}
class FooBar extends Foo{}
class FooBarBat extends FooBar{}
class FooMoo extends Foo{}

$a = new Foo();

if ($a instanceof FooBar && !$a instanceof FooBarBat) {
} elseif ($a instanceof FooMoo) {  
}

FailedTypeResolution error is emitted in the elseif.

It works if && !$a instanceof FooBarBat is not included.

Casting class to string should raise error

Should be errors:

class A {}
echo (new A);
class A {
    function __toString() : void { }
}
echo (new A);
class A {
    function __toString() { }
}
echo (new A);

should not be an error:

class A {
    function __toString() : string {
        return 'hello';
    }
}
echo (new A);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.