diff --git a/src/lint/ArcanistLintMessage.php b/src/lint/ArcanistLintMessage.php index 40502020..d4de43b8 100644 --- a/src/lint/ArcanistLintMessage.php +++ b/src/lint/ArcanistLintMessage.php @@ -1,245 +1,259 @@ setPath($dict['path']); $message->setLine($dict['line']); $message->setChar($dict['char']); $message->setCode($dict['code']); $message->setSeverity($dict['severity']); $message->setName($dict['name']); $message->setDescription($dict['description']); if (isset($dict['original'])) { $message->setOriginalText($dict['original']); } if (isset($dict['replacement'])) { $message->setReplacementText($dict['replacement']); } $message->setGranularity(idx($dict, 'granularity')); $message->setOtherLocations(idx($dict, 'locations', array())); if (isset($dict['bypassChangedLineFiltering'])) { $message->bypassChangedLineFiltering($dict['bypassChangedLineFiltering']); } return $message; } public function toDictionary() { return array( 'path' => $this->getPath(), 'line' => $this->getLine(), 'char' => $this->getChar(), 'code' => $this->getCode(), 'severity' => $this->getSeverity(), 'name' => $this->getName(), 'description' => $this->getDescription(), 'original' => $this->getOriginalText(), 'replacement' => $this->getReplacementText(), 'granularity' => $this->getGranularity(), 'locations' => $this->getOtherLocations(), 'bypassChangedLineFiltering' => $this->shouldBypassChangedLineFiltering(), ); } public function setPath($path) { $this->path = $path; return $this; } public function getPath() { return $this->path; } public function setLine($line) { - if ($line === null) { - // This just means that we don't have any line information. - } else { - // For compatibility, accept digit strings since a lot of linters pass - // line numbers that they have parsed from command output or XML, which - // won't be properly typed. - if (is_string($line) && preg_match('/^\d+\z/', $line)) { - $line = (int)$line; - } - - if (!is_int($line)) { - throw new Exception( - pht( - 'Parameter passed to setLine() must be an integer.')); - } - } - - $this->line = $line; + $this->line = $this->validateInteger($line, 'setLine'); return $this; } public function getLine() { return $this->line; } public function setChar($char) { - $this->char = $char; + $this->char = $this->validateInteger($char, 'setChar'); return $this; } public function getChar() { return $this->char; } public function setCode($code) { $this->code = $code; return $this; } public function getCode() { return $this->code; } public function setSeverity($severity) { $this->severity = $severity; return $this; } public function getSeverity() { return $this->severity; } public function setName($name) { $this->name = $name; return $this; } public function getName() { return $this->name; } public function setDescription($description) { $this->description = $description; return $this; } public function getDescription() { return $this->description; } public function setOriginalText($original) { $this->originalText = $original; return $this; } public function getOriginalText() { return $this->originalText; } public function setReplacementText($replacement) { $this->replacementText = $replacement; return $this; } public function getReplacementText() { return $this->replacementText; } /** * @param dict Keys 'path', 'line', 'char', 'original'. */ public function setOtherLocations(array $locations) { assert_instances_of($locations, 'array'); $this->otherLocations = $locations; return $this; } public function getOtherLocations() { return $this->otherLocations; } public function isError() { return $this->getSeverity() == ArcanistLintSeverity::SEVERITY_ERROR; } public function isWarning() { return $this->getSeverity() == ArcanistLintSeverity::SEVERITY_WARNING; } public function isAutofix() { return $this->getSeverity() == ArcanistLintSeverity::SEVERITY_AUTOFIX; } public function hasFileContext() { return ($this->getLine() !== null); } public function setObsolete($obsolete) { $this->obsolete = $obsolete; return $this; } public function getObsolete() { return $this->obsolete; } public function isPatchable() { return ($this->getReplacementText() !== null) && ($this->getReplacementText() !== $this->getOriginalText()); } public function didApplyPatch() { if ($this->appliedToDisk) { return $this; } $this->appliedToDisk = true; foreach ($this->dependentMessages as $message) { $message->didApplyPatch(); } return $this; } public function isPatchApplied() { return $this->appliedToDisk; } public function setGranularity($granularity) { $this->granularity = $granularity; return $this; } public function getGranularity() { return $this->granularity; } public function setDependentMessages(array $messages) { assert_instances_of($messages, __CLASS__); $this->dependentMessages = $messages; return $this; } public function setBypassChangedLineFiltering($bypass_changed_lines) { $this->bypassChangedLineFiltering = $bypass_changed_lines; return $this; } public function shouldBypassChangedLineFiltering() { return $this->bypassChangedLineFiltering; } + /** + * Validate an integer-like value, returning a strict integer. + * + * Further on, the pipeline is strict about types. We want to be a little + * less strict in linters themselves, since they often parse command line + * output or XML and will end up with string representations of numbers. + * + * @param mixed Integer or digit string. + * @return int Integer. + */ + private function validateInteger($value, $caller) { + if ($value === null) { + // This just means that we don't have any information. + return null; + } + + // Strings like "234" are fine, coerce them to integers. + if (is_string($value) && preg_match('/^\d+\z/', $value)) { + $value = (int)$value; + } + + if (!is_int($value)) { + throw new Exception( + pht( + 'Parameter passed to "%s" must be an integer.', + $caller.'()')); + } + + return $value; + } + }