Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 125 additions & 30 deletions lint/linter/PhpstanLinter.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,22 @@ protected function getMandatoryFlags()
return $flags;
}

protected function getDependencyFlags()
{
$flags = array(
'dump-deps',
'--no-progress',
);
if (null !== $this->configFile) {
array_push($flags, '-c', $this->configFile);
}
if (null !== $this->autoloadFile) {
array_push($flags, '-a', $this->autoloadFile);
}

return $flags;
}

public function getLinterConfigurationOptions()
{
$options = array(
Expand Down Expand Up @@ -129,18 +145,18 @@ public function getLinterConfigurationOptions()
public function setLinterConfigurationValue($key, $value)
{
switch ($key) {
case 'config':
$this->configFile = $value;
return;
case 'level':
$this->level = $value;
return;
case 'autoload':
$this->autoloadFile = $value;
return;
default:
parent::setLinterConfigurationValue($key, $value);
return;
case 'config':
$this->configFile = $value;
return;
case 'level':
$this->level = $value;
return;
case 'autoload':
$this->autoloadFile = $value;
return;
default:
parent::setLinterConfigurationValue($key, $value);
return;
}
}

Expand All @@ -151,15 +167,21 @@ protected function getDefaultMessageSeverity($code)

protected function parseLinterOutput($path, $err, $stdout, $stderr)
{
if ($err === 0) {
return array();
}

$result = array();
if (!empty($stdout)) {
$stdout = substr($stdout, strpos($stdout, '<?xml'));
$checkstyleOutpout = new SimpleXMLElement($stdout);
$errors = $checkstyleOutpout->xpath('//file/error');
foreach($errors as $error) {
$violation = $this->parseViolation($error);
$violation['path'] = $path;
$files = $checkstyleOutpout->xpath('//file');
foreach ($files as $file) {
$path = (string)$file['name'];
foreach($file->error as $error) {
$violation = $this->parseViolation($error, $path);
$result[] = ArcanistLintMessage::newFromDictionary($violation);
}
}
}

Expand All @@ -177,31 +199,34 @@ protected function parseLinterOutput($path, $err, $stdout, $stderr)
* </checkstyle>
*
* Of this, we need to extract
* - Line
* - Line
* - Column
* - Severity
* - Message
* - Source (name)
*
* @param SimpleXMLElement $violation The XML Entity containing the issue
*
* @return array of the form
* [
* 'code' => {string},
* 'name' => {string},
* 'line' => {int},
* 'column' => {int},
* 'char' => {int},
* 'severity' => {string},
* 'message' => {string}
* 'description' => {string},
* 'path' => {string}
* ]
*/
private function parseViolation(SimpleXMLElement $violation)
private function parseViolation(SimpleXMLElement $violation, $path)
{
return array(
'code' => $this->getLinterName(),
'name' => (string)$violation['message'],
'line' => (int)$violation['line'],
'char' => (int)$violation['column'],
'severity' => $this->getMatchSeverity((string)$violation['severity']),
'description' => (string)$violation['message']
'description' => (string)$violation['message'],
'path' => (string)$path,
);
}

Expand All @@ -226,16 +251,86 @@ public function getLinterName()
*/
private function getMatchSeverity($severity_name)
{
$map = array(
'error' => ArcanistLintSeverity::SEVERITY_ERROR,
'warning' => ArcanistLintSeverity::SEVERITY_WARNING,
'info' => ArcanistLintSeverity::SEVERITY_ADVICE,
);
foreach ($map as $name => $severity) {
if ($severity_name == $name) {
return $severity;
$map = array(
'error' => ArcanistLintSeverity::SEVERITY_ERROR,
'warning' => ArcanistLintSeverity::SEVERITY_WARNING,
'info' => ArcanistLintSeverity::SEVERITY_ADVICE,
);
foreach ($map as $name => $severity) {
if ($severity_name == $name) {
return $severity;
}
}
return ArcanistLintSeverity::SEVERITY_ERROR;
}

public function willLintPaths(array $paths)
{
$dependentPaths = $this->getDependentPaths($paths);

$commandFlags = $this->getCommandFlagsWithPaths(array_merge($paths, $dependentPaths));

list($err, $stdout, $stderr) = exec_manual('%C %Ls', $this->getExecutableCommand(), $commandFlags);

$messages = $this->parseLinterOutput('unused', $err, $stdout, $stderr);

foreach ($messages as $message) {
$this->addLintMessage($message);
}

return;
}

public function didLintPaths(array $paths)
{
return;
}

/**
* @param array $paths
* @return array
*/
private function getDependentPaths(array $paths)
{
list($stdout, $stderr) = execx('%C %Ls', $this->getExecutableCommand(), $this->getDependencyFlags());
$dependencies = json_decode($stdout);

$root = $this->getProjectRoot();

$additionalPaths = array();
foreach ($paths as $pathToLint) {
$fullPathToLint = Filesystem::resolvePath($pathToLint, $root);

if (property_exists($dependencies, $fullPathToLint)) {
$additionalPaths = array_merge($additionalPaths, $dependencies->$fullPathToLint);
}
}

$additionalPaths = array_unique($additionalPaths);

$additionalPaths = array_map(
function ($path) use ($root) {
return str_replace($root . '/', '', $path);
},
$additionalPaths
);

return $additionalPaths;
}

/**
* @param $allPathsToLint
* @return array
*/
private function getCommandFlagsWithPaths($allPathsToLint)
{
$commandFlags = $this->getCommandFlags();
$commandFlags[] = '--';
foreach (array_unique($allPathsToLint) as $pathToLint) {
$commandFlags[] = $pathToLint;
}

return $commandFlags;
}

}