diff --git a/.travis.yml b/.travis.yml
index f99927298..1380e6da0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ install:
script:
- vendor/bin/phpunit
- - bin/psalm
+ - ./psalm
- vendor/bin/phpcs
after_success:
diff --git a/composer.json b/composer.json
index 5666101c8..bf469f3bd 100644
--- a/composer.json
+++ b/composer.json
@@ -16,7 +16,7 @@
"openlss/lib-array2xml": "^0.0.10||^0.5.1",
"muglug/package-versions": "^1.2.1"
},
- "bin": ["bin/psalm"],
+ "bin": ["psalm"],
"autoload": {
"psr-4": {
"Psalm\\": "src/Psalm"
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 595700027..2425f21df 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -38,6 +38,7 @@
./src/Psalm/Stubs/
./src/Psalm/CallMap.php
+ ./src/psalm.php
./src/Psalm/PropertyMap.php
./src/Psalm/Issue/
diff --git a/psalm b/psalm
new file mode 100755
index 000000000..b6546edd8
--- /dev/null
+++ b/psalm
@@ -0,0 +1,2 @@
+#!/usr/bin/env php
+
+
+
+
+
+
+
+
+
@@ -46,6 +54,7 @@
+
diff --git a/src/Psalm/CallMap.php b/src/Psalm/CallMap.php
index f26ce97c3..345236a94 100644
--- a/src/Psalm/CallMap.php
+++ b/src/Psalm/CallMap.php
@@ -2507,7 +2507,7 @@ return [
'getmypid' => ['int'],
'getmyuid' => ['int'],
'get_object_vars' => ['array', 'obj'=>'object'],
-'getopt' => ['array', 'options'=>'string', 'longopts='=>'array', '&w_optind='=>'int'],
+'getopt' => ['(string|false|array)[]', 'options'=>'string', 'longopts='=>'array', '&w_optind='=>'int'],
'get_parent_class' => ['', 'object='=>''],
'getprotobyname' => ['int|false', 'name'=>'string'],
'getprotobynumber' => ['string', 'proto'=>'int'],
@@ -7922,7 +7922,7 @@ return [
'SCA::createDataObject' => ['SDO_DataObject', 'type_namespace_uri'=>'string', 'type_name'=>'string'],
'SCA::getService' => ['', 'target'=>'string', 'binding='=>'string', 'config='=>'array'],
'SCA_LocalProxy::createDataObject' => ['SDO_DataObject', 'type_namespace_uri'=>'string', 'type_name'=>'string'],
-'scandir' => ['array', 'dir'=>'string', 'sorting_order='=>'int', 'context='=>''],
+'scandir' => ['string[]|false', 'dir'=>'string', 'sorting_order='=>'int', 'context='=>''],
'SCA_SoapProxy::createDataObject' => ['SDO_DataObject', 'type_namespace_uri'=>'string', 'type_name'=>'string'],
'SDO_DAS_ChangeSummary::beginLogging' => [''],
'SDO_DAS_ChangeSummary::endLogging' => [''],
diff --git a/src/Psalm/Checker/FileChecker.php b/src/Psalm/Checker/FileChecker.php
index 47710e984..b232d5422 100644
--- a/src/Psalm/Checker/FileChecker.php
+++ b/src/Psalm/Checker/FileChecker.php
@@ -387,18 +387,6 @@ class FileChecker extends SourceChecker implements StatementsSource
$this->analyze($file_context);
}
- /**
- * Used when checking single files with multiple classlike declarations
- *
- * @param string $fq_class_name
- *
- * @return bool
- */
- public function containsUnEvaluatedClassLike($fq_class_name)
- {
- return false;
- }
-
/**
* @return array
*/
diff --git a/src/Psalm/Checker/FunctionLikeChecker.php b/src/Psalm/Checker/FunctionLikeChecker.php
index fac0a2914..c95b612c7 100644
--- a/src/Psalm/Checker/FunctionLikeChecker.php
+++ b/src/Psalm/Checker/FunctionLikeChecker.php
@@ -179,8 +179,6 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
}
if ($overridden_method_ids && $this->function->name !== '__construct') {
- $have_emitted = false;
-
foreach ($overridden_method_ids as $overridden_method_id) {
$parent_method_storage = MethodChecker::getStorage($project_checker, $overridden_method_id);
@@ -564,7 +562,6 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
) {
$implementer_method_id = $implementer_classlike_storage->name . '::'
. strtolower($guide_method_storage->cased_name);
- $guide_method_id = $guide_classlike_storage->name . '::' . strtolower($guide_method_storage->cased_name);
$implementer_declaring_method_id = MethodChecker::getDeclaringMethodId(
$project_checker,
$implementer_method_id
diff --git a/src/Psalm/Checker/Statements/Block/TryChecker.php b/src/Psalm/Checker/Statements/Block/TryChecker.php
index 5562e8695..641d5d92b 100644
--- a/src/Psalm/Checker/Statements/Block/TryChecker.php
+++ b/src/Psalm/Checker/Statements/Block/TryChecker.php
@@ -115,8 +115,6 @@ class TryChecker
}
}
- $exception_type = new Union([new TNamedObject('Exception'), new TNamedObject('Throwable')]);
-
if ((ClassChecker::classExists($project_checker, $fq_catch_class)
&& strtolower($fq_catch_class) !== 'exception'
&& !(ClassChecker::classExtends($project_checker, $fq_catch_class, 'Exception')
diff --git a/src/Psalm/Checker/Statements/Expression/FetchChecker.php b/src/Psalm/Checker/Statements/Expression/FetchChecker.php
index 861e82a5b..d38b7b65b 100644
--- a/src/Psalm/Checker/Statements/Expression/FetchChecker.php
+++ b/src/Psalm/Checker/Statements/Expression/FetchChecker.php
@@ -438,6 +438,10 @@ class FetchChecker
$stmt->inferredType = Type::getTrue();
break;
+ case 'stdin':
+ $stmt->inferredType = Type::getResource();
+ break;
+
default:
$const_type = $statements_checker->getConstType(
$statements_checker,
@@ -509,15 +513,6 @@ class FetchChecker
$statements_checker->getAliases()
);
- // edge case when evaluating single files
- if ($stmt->name === 'class' &&
- $statements_checker->getFileChecker()->containsUnEvaluatedClassLike($fq_class_name)
- ) {
- $stmt->inferredType = Type::getString();
-
- return null;
- }
-
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
$statements_checker->getFileChecker()->project_checker,
$fq_class_name,
diff --git a/src/Psalm/Checker/TraitChecker.php b/src/Psalm/Checker/TraitChecker.php
index 2d6948fbe..d77ab5387 100644
--- a/src/Psalm/Checker/TraitChecker.php
+++ b/src/Psalm/Checker/TraitChecker.php
@@ -6,11 +6,6 @@ use Psalm\TraitSource;
class TraitChecker extends ClassLikeChecker
{
- /**
- * @var array
- */
- private $method_map = [];
-
/**
* @param PhpParser\Node\Stmt\Trait_ $class
* @param TraitSource $trait_source
@@ -27,16 +22,6 @@ class TraitChecker extends ClassLikeChecker
self::$trait_checkers[strtolower($fq_class_name)] = $this;
}
- /**
- * @param array $method_map
- *
- * @return void
- */
- public function setMethodMap(array $method_map)
- {
- $this->method_map = $method_map;
- }
-
/**
* @param string $fq_trait_name
* @param FileChecker $file_checker
diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php
index 1378fc301..8a04f9952 100644
--- a/src/Psalm/Config.php
+++ b/src/Psalm/Config.php
@@ -754,4 +754,33 @@ class Config
return $composer_classmap;
}
+
+ /**
+ * @param string $dir
+ *
+ * @return void
+ */
+ public static function removeCacheDirectory($dir)
+ {
+ if (is_dir($dir)) {
+ $objects = scandir($dir);
+
+ if ($objects === false) {
+ throw new \UnexpectedValueException('Not expecting false here');
+ }
+
+ foreach ($objects as $object) {
+ if ($object != '.' && $object != '..') {
+ if (filetype($dir . '/' . $object) == 'dir') {
+ self::removeCacheDirectory($dir . '/' . $object);
+ } else {
+ unlink($dir . '/' . $object);
+ }
+ }
+ }
+
+ reset($objects);
+ rmdir($dir);
+ }
+ }
}
diff --git a/src/Psalm/IssueBuffer.php b/src/Psalm/IssueBuffer.php
index 895a522ba..955118984 100644
--- a/src/Psalm/IssueBuffer.php
+++ b/src/Psalm/IssueBuffer.php
@@ -35,7 +35,7 @@ class IssueBuffer
protected static $recorded_issues = [];
/**
- * @var int
+ * @var float
*/
protected static $start_time = 0;
@@ -301,7 +301,7 @@ class IssueBuffer
}
/**
- * @param int $time
+ * @param float $time
*
* @return void
*/
diff --git a/src/Psalm/Type.php b/src/Psalm/Type.php
index 5472fb609..f38a19e2a 100644
--- a/src/Psalm/Type.php
+++ b/src/Psalm/Type.php
@@ -16,6 +16,7 @@ use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
+use Psalm\Type\Atomic\TResource;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTrue;
use Psalm\Type\Atomic\TVoid;
@@ -426,6 +427,14 @@ abstract class Type
return new Union([$type]);
}
+ /**
+ * @return Type\Union
+ */
+ public static function getResource()
+ {
+ return new Union([new TResource]);
+ }
+
/**
* @param array $redefined_vars
* @param Context $context
diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php
index 0bd9e1ec0..c8384c272 100644
--- a/src/Psalm/Type/Atomic/GenericTrait.php
+++ b/src/Psalm/Type/Atomic/GenericTrait.php
@@ -128,7 +128,7 @@ trait GenericTrait
*/
public function replaceTemplateTypesWithArgTypes(array $template_types)
{
- foreach ($this->type_params as $offset => $type_param) {
+ foreach ($this->type_params as $type_param) {
$type_param->replaceTemplateTypesWithArgTypes($template_types);
}
}
diff --git a/src/Psalm/Type/ParseTree.php b/src/Psalm/Type/ParseTree.php
index 44aeac276..e3aa2d1a8 100644
--- a/src/Psalm/Type/ParseTree.php
+++ b/src/Psalm/Type/ParseTree.php
@@ -149,7 +149,6 @@ class ParseTree
array_pop($current_parent->children);
$current_parent->children[] = $new_parent_leaf;
- $has_object_key = true;
break;
diff --git a/bin/psalm b/src/psalm.php
old mode 100755
new mode 100644
similarity index 84%
rename from bin/psalm
rename to src/psalm.php
index a0b8a9c28..e652fdf50
--- a/bin/psalm
+++ b/src/psalm.php
@@ -1,7 +1,5 @@
-#!/usr/bin/env php
$path_to_check) {
if ($path_to_check[0] === '-') {
die('Invalid usage, expecting psalm [options] [file...]' . PHP_EOL);
@@ -305,7 +312,13 @@ if ($input_paths) {
die('Cannot locate ' . $path_to_check . PHP_EOL);
}
- $paths_to_check[] = realpath($path_to_check);
+ $path_to_check = realpath($path_to_check);
+
+ if (!$path_to_check) {
+ die('Error getting realpath for file' . PHP_EOL);
+ }
+
+ $paths_to_check[] = $path_to_check;
}
if (!$paths_to_check) {
@@ -313,10 +326,11 @@ if ($input_paths) {
}
}
-$path_to_config = isset($options['c']) ? realpath($options['c']) : null;
+$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
if ($path_to_config === false) {
- die('Could not resolve path to config ' . $options['c'] . PHP_EOL);
+ /** @psalm-suppress InvalidCast */
+ die('Could not resolve path to config ' . (string)$options['c'] . PHP_EOL);
}
$use_color = !array_key_exists('m', $options);
@@ -329,7 +343,9 @@ $is_diff = isset($options['diff']);
$find_dead_code = isset($options['find-dead-code']);
-$find_references_to = isset($options['find-references-to']) ? $options['find-references-to'] : null;
+$find_references_to = isset($options['find-references-to']) && is_string($options['find-references-to'])
+ ? $options['find-references-to']
+ : null;
$update_docblocks = isset($options['update-docblocks']);
@@ -350,7 +366,7 @@ $project_checker = new ProjectChecker(
$update_docblocks,
$find_dead_code || $find_references_to !== null,
$find_references_to,
- isset($options['report'])?$options['report']:null
+ isset($options['report']) && is_string($options['report']) ? $options['report'] : null
);
// initialise custom config, if passed
@@ -358,46 +374,24 @@ if ($path_to_config) {
$project_checker->setConfigXML($path_to_config, $current_dir);
}
-/**
- * @param string $dir
- * @return void
- */
-function rrmdir($dir)
-{
- if (is_dir($dir)) {
- $objects = scandir($dir);
- foreach ($objects as $object) {
- if ($object != "." && $object != "..") {
- if (filetype($dir."/".$object) == "dir") {
- rrmdir($dir."/".$object);
- } else {
- unlink($dir."/".$object);
- }
- }
- }
-
- reset($objects);
- rmdir($dir);
- }
-}
-
if (isset($options['clear-cache'])) {
// initialise config if it hasn't already been
if (!$path_to_config) {
- $project_checker->getConfigForPath($current_dir, $current_dir);
+ $project_checker->getConfigForPath($current_dir, $current_dir);
}
$cache_directory = Config::getInstance()->getCacheDirectory();
- rrmdir($cache_directory);
+ Config::removeCacheDirectory($cache_directory);
echo 'Cache directory deleted' . PHP_EOL;
exit;
}
+/** @psalm-suppress MixedArgument */
\Psalm\IssueBuffer::setStartTime(microtime(true));
if (array_key_exists('self-check', $options)) {
- $project_checker->checkDir(dirname(__DIR__) . '/src', dirname(__DIR__));
+ $project_checker->checkDir(__DIR__, dirname(__DIR__));
} elseif ($paths_to_check === null) {
$project_checker->check($current_dir, $is_diff);
} elseif ($paths_to_check) {