1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2025-01-10 06:41:04 +01:00

(stable) Promote 2020 Week 21

This commit is contained in:
epriestley 2020-05-30 04:55:54 -07:00
commit d1c07b4d26
13 changed files with 404 additions and 67 deletions

View file

@ -612,6 +612,7 @@ phutil_register_library_map(array(
'PhutilArgumentUsageException' => 'parser/argument/exception/PhutilArgumentUsageException.php', 'PhutilArgumentUsageException' => 'parser/argument/exception/PhutilArgumentUsageException.php',
'PhutilArgumentWorkflow' => 'parser/argument/workflow/PhutilArgumentWorkflow.php', 'PhutilArgumentWorkflow' => 'parser/argument/workflow/PhutilArgumentWorkflow.php',
'PhutilArray' => 'utils/PhutilArray.php', 'PhutilArray' => 'utils/PhutilArray.php',
'PhutilArrayCheck' => 'utils/PhutilArrayCheck.php',
'PhutilArrayTestCase' => 'utils/__tests__/PhutilArrayTestCase.php', 'PhutilArrayTestCase' => 'utils/__tests__/PhutilArrayTestCase.php',
'PhutilArrayWithDefaultValue' => 'utils/PhutilArrayWithDefaultValue.php', 'PhutilArrayWithDefaultValue' => 'utils/PhutilArrayWithDefaultValue.php',
'PhutilAsanaFuture' => 'future/asana/PhutilAsanaFuture.php', 'PhutilAsanaFuture' => 'future/asana/PhutilAsanaFuture.php',
@ -1619,6 +1620,7 @@ phutil_register_library_map(array(
'ArrayAccess', 'ArrayAccess',
'Iterator', 'Iterator',
), ),
'PhutilArrayCheck' => 'Phobject',
'PhutilArrayTestCase' => 'PhutilTestCase', 'PhutilArrayTestCase' => 'PhutilTestCase',
'PhutilArrayWithDefaultValue' => 'PhutilArray', 'PhutilArrayWithDefaultValue' => 'PhutilArray',
'PhutilAsanaFuture' => 'FutureProxy', 'PhutilAsanaFuture' => 'FutureProxy',

View file

@ -261,8 +261,7 @@ final class ArcanistTextLinter extends ArcanistLinter {
self::LINT_TRAILING_WHITESPACE, self::LINT_TRAILING_WHITESPACE,
pht( pht(
'This line contains trailing whitespace. Consider setting '. 'This line contains trailing whitespace. Consider setting '.
'up your editor to automatically remove trailing whitespace, '. 'up your editor to automatically remove trailing whitespace.'),
'you will save time.'),
$string, $string,
''); '');
} }

View file

@ -64,8 +64,6 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
$contents, $contents,
array(null, null)); array(null, null));
$basename = basename($file);
if ($config) { if ($config) {
$config = phutil_json_decode($config); $config = phutil_json_decode($config);
} else { } else {
@ -87,6 +85,14 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
$caught_exception = false; $caught_exception = false;
try { try {
$path_name = idx($config, 'path');
if ($path_name !== null) {
$basename = basename($path_name);
} else {
$basename = basename($file);
}
$tmp = new TempFile($basename); $tmp = new TempFile($basename);
Filesystem::writeFile($tmp, $data); Filesystem::writeFile($tmp, $data);
$full_path = (string)$tmp; $full_path = (string)$tmp;
@ -97,7 +103,6 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
} }
$dir = dirname($full_path); $dir = dirname($full_path);
$path = basename($full_path);
$working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile( $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile(
$dir, $dir,
@ -106,26 +111,25 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
$configuration_manager = new ArcanistConfigurationManager(); $configuration_manager = new ArcanistConfigurationManager();
$configuration_manager->setWorkingCopyIdentity($working_copy); $configuration_manager->setWorkingCopyIdentity($working_copy);
$engine = new ArcanistUnitTestableLintEngine(); $engine = new ArcanistUnitTestableLintEngine();
$engine->setWorkingCopy($working_copy); $engine->setWorkingCopy($working_copy);
$engine->setConfigurationManager($configuration_manager); $engine->setConfigurationManager($configuration_manager);
$path_name = idx($config, 'path', $path); $engine->setPaths(array($basename));
$engine->setPaths(array($path_name));
$linter->setEngine($engine); $linter->setEngine($engine);
$linter->addPath($path_name); $linter->addPath($basename);
$linter->addData($path_name, $data); $linter->addData($basename, $data);
foreach (idx($config, 'config', array()) as $key => $value) { foreach (idx($config, 'config', array()) as $key => $value) {
$linter->setLinterConfigurationValue($key, $value); $linter->setLinterConfigurationValue($key, $value);
} }
$engine->addLinter($linter); $engine->addLinter($linter);
$engine->addFileData($path_name, $data); $engine->addFileData($basename, $data);
$results = $engine->run(); $results = $engine->run();
$this->assertEqual( $this->assertEqual(
1, 1,
count($results), count($results),
@ -187,9 +191,20 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
$message = new ArcanistLintMessage(); $message = new ArcanistLintMessage();
$severity = idx($parts, 0); $severity = idx($parts, 0);
$line = idx($parts, 1); $line = idx($parts, 1);
$char = idx($parts, 2); if ($line === '') {
$code = idx($parts, 3); $line = null;
}
$char = idx($parts, 2);
if ($char === '') {
$char = null;
}
$code = idx($parts, 3);
if ($code === '') {
$code = null;
}
if ($severity !== null) { if ($severity !== null) {
$message->setSeverity($severity); $message->setSeverity($severity);
@ -256,46 +271,14 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
} }
if ($missing || $surprising) { if ($missing || $surprising) {
$expected = pht('EXPECTED MESSAGES');
if ($missing) {
foreach ($missing as $message) {
$expected .= sprintf(
"\n %s: %s %s",
pht(
'%s at line %d, char %d',
$message->getSeverity(),
$message->getLine(),
$message->getChar()),
$message->getCode(),
$message->getName());
}
} else {
$expected .= "\n ".pht('No messages');
}
$actual = pht('UNEXPECTED MESSAGES');
if ($surprising) {
foreach ($surprising as $message) {
$actual .= sprintf(
"\n %s: %s %s",
pht(
'%s at line %d, char %d',
$message->getSeverity(),
$message->getLine(),
$message->getChar()),
$message->getCode(),
$message->getName());
}
} else {
$actual .= "\n ".pht('No messages');
}
$this->assertFailure( $this->assertFailure(
sprintf( sprintf(
"%s\n\n%s\n\n%s", "%s\n%s%s",
pht("Lint failed for '%s'.", $file), pht(
$expected, 'Lint emitted an unexpected set of messages for file "%s".',
$actual)); $file),
$this->renderMessages(pht('MISSING MESSAGES'), $missing),
$this->renderMessages(pht('SURPLUS MESSAGES'), $surprising)));
} }
} }
@ -312,15 +295,78 @@ abstract class ArcanistLinterTestCase extends PhutilTestCase {
/** /**
* Compare properties of @{class:ArcanistLintMessage} instances. * Compare properties of @{class:ArcanistLintMessage} instances.
* *
* The expectation is that if one (or both) of the properties is null, then
* we don't care about its value.
*
* @param wild * @param wild
* @param wild * @param wild
* @return bool * @return bool
*/ */
private static function compareLintMessageProperty($x, $y) { private static function compareLintMessageProperty($x, $y) {
return $x === null || $y === null || $x === $y; if ($x === null) {
return true;
}
return ($x === $y);
}
private function renderMessages($header, array $messages) {
if (!$messages) {
$display = tsprintf(
"%s\n",
pht('(No messages.)'));
} else {
$lines = array();
foreach ($messages as $message) {
$line = $message->getLine();
if ($line === null) {
$display_line = pht('<null>');
} else {
$display_line = $line;
}
$char = $message->getChar();
if ($char === null) {
$display_char = pht('<null>');
} else {
$display_char = $char;
}
$code = $message->getCode();
$name = $message->getName();
if ($code !== null && $name !== null) {
$display_code = pht('%s: %s', $code, $name);
} else if ($code !== null) {
$display_code = pht('%s', $code);
} else {
$display_code = null;
}
$severity = $message->getSeverity();
if ($display_code === null) {
$display_message = pht(
'Message with severity "%s" at "%s:%s"',
$severity,
$display_line,
$display_char);
} else {
$display_message = pht(
'Message with severity "%s" at "%s:%s" (%s)',
$severity,
$display_line,
$display_char,
$display_code);
}
$lines[] = tsprintf(
" %s\n",
$display_message);
}
$display = implode('', $lines);
}
return tsprintf(
"%s\n%B\n",
$header,
$display);
} }
} }

View file

@ -1,5 +1,5 @@
~~~~~~~~~~ ~~~~~~~~~~
warning:0:0:CHMOD1:Invalid Executable warning:::CHMOD1:Invalid Executable
~~~~~~~~~~ ~~~~~~~~~~
~~~~~~~~~~ ~~~~~~~~~~
{"mode": "0755"} {"mode": "0755"}

View file

@ -1,10 +1,21 @@
#include "library.cpp" #include "library.cpp"
#include <stdio> #include <stdio>
void main()
{
}
~~~~~~~~~
warning
warning:2
warning:5
~~~~~~~~~~
#include "library.cpp"
#include <stdio>
void main() void main()
{ {
} }
~~~~~~~~~~ ~~~~~~~~~~
warning:0: {
warning:2: "path": "example.cpp"
warning:5: }

View file

@ -1,5 +1,5 @@
~~~~~~~~~~ ~~~~~~~~~~
error:0:0:NAME1:Bad Filename error:::NAME1:Bad Filename
~~~~~~~~~~ ~~~~~~~~~~
~~~~~~~~~~ ~~~~~~~~~~
{"path": "bad@filename"} {"path": "bad@filename"}

View file

@ -4,4 +4,4 @@ function f() {
$this = "cannot be re-assigned"; $this = "cannot be re-assigned";
} }
~~~~~~~~~ ~~~~~~~~~
error:4:0:PHP2:Fatal Error error:4::PHP2:Fatal Error

View file

@ -4,4 +4,4 @@ function f() {
this is bad syntax; this is bad syntax;
} }
~~~~~~~~~ ~~~~~~~~~
error:4:0:PHP1:Parse Error error:4::PHP1:Parse Error

View file

@ -1,4 +1,4 @@
def hello() def hello()
puts "hello world" puts "hello world"
~~~~~~~~~~ ~~~~~~~~~~
error:2:0:RUBY:Syntax Error error:2::RUBY:Syntax Error

View file

@ -1,4 +1,4 @@
~~~~~~~~~~ ~~~~~~~~~~
error:0:0:TXT10:Empty File error:::TXT10:Empty File

View file

@ -1,4 +1,4 @@
<?php <?php
~~~~~~~~~~ ~~~~~~~~~~
warning:0:0:XHP82:Empty File warning:::XHP82:Empty File

View file

@ -0,0 +1,278 @@
<?php
final class PhutilArrayCheck
extends Phobject {
private $instancesOf;
private $uniqueMethod;
private $context;
private $object;
private $method;
public function setInstancesOf($instances_of) {
$this->instancesOf = $instances_of;
return $this;
}
public function getInstancesOf() {
return $this->instancesOf;
}
public function setUniqueMethod($unique_method) {
$this->uniqueMethod = $unique_method;
return $this;
}
public function getUniqueMethod() {
return $this->uniqueMethod;
}
public function setContext($object, $method) {
if (is_array($object)) {
foreach ($object as $idx => $value) {
if (!is_object($value)) {
throw new Exception(
pht(
'Expected an object, string, or list of objects for "object" '.
'context. Got a list ("%s"), but the list item at index '.
'"%s" (with type "%s") is not an object.',
phutil_describe_type($object),
$idx,
phutil_describe_type($value)));
}
}
} else if (!is_object($object) && !is_string($object)) {
throw new Exception(
pht(
'Expected an object, string, or list of objects for "object" '.
'context, got "%s".',
phutil_describe_type($object)));
}
if (!is_string($method)) {
throw new Exception(
pht(
'Expected a string for "method" context, got "%s".',
phutil_describe_type($method)));
}
$argv = func_get_args();
$argv = array_slice($argv, 2);
$this->context = array(
'object' => $object,
'method' => $method,
'argv' => $argv,
);
return $this;
}
public function checkValues($maps) {
foreach ($maps as $idx => $map) {
$maps[$idx] = $this->checkValue($map);
}
$unique = $this->getUniqueMethod();
if ($unique === null) {
$result = array();
foreach ($maps as $map) {
foreach ($map as $value) {
$result[] = $value;
}
}
} else {
$items = array();
foreach ($maps as $idx => $map) {
foreach ($map as $key => $value) {
$items[$key][$idx] = $value;
}
}
foreach ($items as $key => $values) {
if (count($values) === 1) {
continue;
}
$this->raiseValueException(
pht(
'Unexpected return value from calls to "%s(...)". More than one '.
'object returned a value with unique key "%s". This key was '.
'returned by objects with indexes: %s.',
$unique,
$key,
implode(', ', array_keys($values))));
}
$result = array();
foreach ($items as $key => $values) {
$result[$key] = head($values);
}
}
return $result;
}
public function checkValue($items) {
if (!$this->context) {
throw new PhutilInvalidStateException('setContext');
}
if (!is_array($items)) {
$this->raiseValueException(
pht(
'Expected value to be a list, got "%s".',
phutil_describe_type($items)));
}
$instances_of = $this->getInstancesOf();
if ($instances_of !== null) {
foreach ($items as $idx => $item) {
if (!($item instanceof $instances_of)) {
$this->raiseValueException(
pht(
'Expected value to be a list of objects which are instances of '.
'"%s", but item with index "%s" is "%s".',
$instances_of,
$idx,
phutil_describe_type($item)));
}
}
}
$unique = $this->getUniqueMethod();
if ($unique !== null) {
if ($instances_of === null) {
foreach ($items as $idx => $item) {
if (!is_object($item)) {
$this->raiseValueException(
pht(
'Expected value to be a list of objects to support calling '.
'"%s" to generate unique keys, but item with index "%s" is '.
'"%s".',
$unique,
$idx,
phutil_describe_type($item)));
}
}
}
$map = array();
foreach ($items as $idx => $item) {
$key = call_user_func(array($item, $unique));
if (!is_string($key) && !is_int($key)) {
$this->raiseValueException(
pht(
'Expected method "%s->%s()" to return a string or integer for '.
'use as a unique key, got "%s" from object at index "%s".',
get_class($item),
$unique,
phutil_describe_type($key),
$idx));
}
$key = phutil_string_cast($key);
$map[$key][$idx] = $item;
}
$result = array();
foreach ($map as $key => $values) {
if (count($values) === 1) {
$result[$key] = head($values);
continue;
}
$classes = array();
foreach ($values as $value) {
$classes[] = get_class($value);
}
$classes = array_fuse($classes);
if (count($classes) === 1) {
$class_display = head($classes);
} else {
$class_display = sprintf(
'[%s]',
implode(', ', $classes));
}
$index_display = array();
foreach ($values as $idx => $value) {
$index_display[] = pht(
'"%s" (%s)',
$idx,
get_class($value));
}
$index_display = implode(', ', $index_display);
$this->raiseValueException(
pht(
'Expected method "%s->%s()" to return a unique key, got "%s" '.
'from %s object(s) at indexes: %s.',
$class_display,
$unique,
$key,
phutil_count($values),
$index_display));
}
$items = $result;
}
return $items;
}
private function raiseValueException($message) {
$context = $this->context;
$object = $context['object'];
$method = $context['method'];
$argv = $context['argv'];
if (is_array($object)) {
$classes = array();
foreach ($object as $item) {
$classes[] = get_class($item);
}
$classes = array_fuse($classes);
$n = count($object);
$object_display = sprintf(
'[%s]<%d>->%s',
implode(', ', $classes),
$n,
$method);
} else if (is_object($object)) {
$object_display = sprintf(
'%s->%s',
get_class($object),
$method);
} else {
$object_display = sprintf(
'%s::%s',
$object,
$method);
}
if (count($argv)) {
$call_display = sprintf(
'%s(...)',
$object_display);
} else {
$call_display = sprintf(
'%s()',
$object_display);
}
throw new Exception(
pht(
'Unexpected return value from call to "%s": %s.',
$call_display,
$message));
}
}

View file

@ -29,7 +29,8 @@ EOTEXT
->setHelp( ->setHelp(
pht('Perform a clean rebuild, ignoring caches. Thorough, but slow.')), pht('Perform a clean rebuild, ignoring caches. Thorough, but slow.')),
$this->newWorkflowArgument('argv') $this->newWorkflowArgument('argv')
->setWildcard(true), ->setWildcard(true)
->setIsPathArgument(true),
); );
} }