diff --git a/src/Errors/SyntaxError.php b/src/Errors/SyntaxError.php index 9a4a36e..18023e2 100644 --- a/src/Errors/SyntaxError.php +++ b/src/Errors/SyntaxError.php @@ -6,6 +6,8 @@ class SyntaxError extends ParallelLintError { + const IN_ON_REGEX = '~ in %s on line [0-9]+$~'; + /** @var Blame */ private $blame; @@ -29,8 +31,22 @@ public function getLine() */ public function getNormalizedMessage($translateTokens = false) { - $message = preg_replace('~^(Parse|Fatal) error: (syntax error, )?~', '', $this->message); - $message = preg_replace('~ in ' . preg_quote(basename($this->filePath)) . ' on line [0-9]+$~', '', $message); + $message = preg_replace('~^(?:Parse|Fatal) error: (?:syntax error, )?~', '', $this->message); + $baseName = basename($this->filePath); + $regex = sprintf(self::IN_ON_REGEX, preg_quote($baseName, '~')); + $message = preg_replace($regex, '', $message, -1, $count); + + if ($count === 0 && strpos($baseName, '\\') !== false) { + $baseName = ltrim(strrchr($this->filePath, '\\'), '\\'); + $regex = sprintf(self::IN_ON_REGEX, preg_quote($baseName, '~')); + $message = preg_replace($regex, '', $message, -1, $count); + } + + if ($count === 0) { + $regex = sprintf(self::IN_ON_REGEX, preg_quote($this->filePath, '~')); + $message = preg_replace($regex, '', $message); + } + $message = ucfirst($message); if ($translateTokens) { diff --git a/tests/Unit/Errors/SyntaxErrorGetNormalizeMessageTest.php b/tests/Unit/Errors/SyntaxErrorGetNormalizeMessageTest.php index 6e3a88e..8eb47aa 100644 --- a/tests/Unit/Errors/SyntaxErrorGetNormalizeMessageTest.php +++ b/tests/Unit/Errors/SyntaxErrorGetNormalizeMessageTest.php @@ -7,17 +7,119 @@ class SyntaxErrorGetNormalizeMessageTest extends UnitTestCase { - public function testInWordInErrorMessage() + const FILEPATH_MSG_TEMPLATE = "Parse error: unexpected 'Foo' (T_STRING) in %s on line 2"; + const FILEPATH_MSG_EXPECTED = "Unexpected 'Foo' (T_STRING)"; + + /** + * Test retrieving a normalized error message. + * + * @dataProvider dataMessageNormalization + * + * @param string $message The message input to run the test with. + * @param string $expected The expected method return value. + * + * @return void + */ + public function testMessageNormalizationWithoutTokenTranslation($message, $expected) + { + $error = new SyntaxError('test.php', $message); + $this->assertSame($expected, $error->getNormalizedMessage()); + } + + /** + * Data provider. + * + * @return array + */ + public function dataMessageNormalization() + { + return array( + 'Strip leading and trailing information - fatal error' => array( + 'message' => "Fatal error: 'break' not in the 'loop' or 'switch' context in test.php on line 2", + 'expected' => "'break' not in the 'loop' or 'switch' context", + ), + 'Strip leading and trailing information - parse error' => array( + 'message' => "Parse error: unexpected 'Foo' (T_STRING) in test.php on line 2", + 'expected' => "Unexpected 'Foo' (T_STRING)", // Also verifies ucfirst() call is being made. + ), + 'Strip trailing information, not leading - deprecation' => array( + 'message' => "Deprecated: The (real) cast is deprecated, use (float) instead in test.php on line 2", + 'expected' => "Deprecated: The (real) cast is deprecated, use (float) instead", + ), + ); + } + + /** + * Test retrieving a normalized error message with token translation. + * + * @return void + */ + public function testMessageNormalizationWithTokenTranslation() { - $message = 'Fatal error: \'break\' not in the \'loop\' or \'switch\' context in test.php on line 2'; + $message = 'Parse error: unexpected T_FILE, expecting T_STRING in test.php on line 2'; + $expected = 'Unexpected __FILE__ (T_FILE), expecting T_STRING'; + $error = new SyntaxError('test.php', $message); - $this->assertSame('\'break\' not in the \'loop\' or \'switch\' context', $error->getNormalizedMessage()); + $this->assertSame($expected, $error->getNormalizedMessage(true)); + } + + /** + * Test retrieving a normalized error message with variations for the file path. + * + * @dataProvider dataFilePathHandling + * + * @param string $filePath The file path input to run the test with. + * @param string $fileName The file name which is expected to be in the error message. + * + * @return void + */ + public function testFilePathHandling($filePath, $fileName) + { + $message = sprintf(self::FILEPATH_MSG_TEMPLATE, $fileName); + $error = new SyntaxError($filePath, $message); + $this->assertSame(self::FILEPATH_MSG_EXPECTED, $error->getNormalizedMessage()); } - public function testInWordInErrorMessageAndInFileName() + /** + * Data provider. + * + * @return array + */ + public function dataFilePathHandling() { - $message = 'Fatal error: \'break\' not in the \'loop\' or \'switch\' context in test in file.php on line 2'; - $error = new SyntaxError('test in file.php', $message); - $this->assertSame('\'break\' not in the \'loop\' or \'switch\' context', $error->getNormalizedMessage()); + return array( + 'Plain file name' => array( + 'filePath' => 'test.php', + 'fileName' => 'test.php', + ), + 'File name containing spaces' => array( + 'filePath' => 'test in file.php', + 'fileName' => 'test in file.php', + ), + 'File name containing regex delimiter' => array( + 'filePath' => 'test~file.php', + 'fileName' => 'test~file.php', + ), + 'Full file path, linux slashes' => array( + 'filePath' => 'path/to/subdir/file.php', + 'fileName' => 'file.php', + ), + 'File path, windows slashes' => array( + 'filePath' => 'path\to\subdir\file.php', + 'fileName' => 'file.php', + ), + 'Absolute file path, windows slashes' => array( + 'filePath' => 'C:\path\to\subdir\file.php', + 'fileName' => 'C:\path\to\subdir\file.php', + ), + 'Relative file path, windows slashes' => array( + 'filePath' => '.\subdir\file.php', + 'fileName' => '.\subdir\file.php', + ), + 'Phar file name' => array( + 'filePath' => 'phar://031.phar.php/a.php', + 'fileName' => 'phar://031.phar.php/a.php', + ), + ); } }