diff --git a/src/applications/differential/parser/__tests__/DifferentialCustomFieldRevertsParserTestCase.php b/src/applications/differential/parser/__tests__/DifferentialCustomFieldRevertsParserTestCase.php index ff8d1c1bf7..0061273ac7 100644 --- a/src/applications/differential/parser/__tests__/DifferentialCustomFieldRevertsParserTestCase.php +++ b/src/applications/differential/parser/__tests__/DifferentialCustomFieldRevertsParserTestCase.php @@ -1,92 +1,95 @@ array(), // Git default message. 'This reverts commit 1234abcd.' => array( array( 'match' => 'reverts commit 1234abcd', 'prefix' => 'reverts', 'infix' => 'commit', 'monograms' => array('1234abcd'), 'suffix' => '', 'offset' => 5, ), ), // Mercurial default message. 'Backed out changeset 1234abcd.' => array( array( 'match' => 'Backed out changeset 1234abcd', 'prefix' => 'Backed out', 'infix' => 'changeset', 'monograms' => array('1234abcd'), 'suffix' => '', 'offset' => 0, ), ), 'this undoes 1234abcd, 5678efab. they were bad' => array( array( 'match' => 'undoes 1234abcd, 5678efab', 'prefix' => 'undoes', 'infix' => '', 'monograms' => array('1234abcd', '5678efab'), 'suffix' => '', 'offset' => 5, ), ), 'Reverts 123' => array( array( 'match' => 'Reverts 123', 'prefix' => 'Reverts', 'infix' => '', 'monograms' => array('123'), 'suffix' => '', 'offset' => 0, ), ), 'Reverts r123' => array( array( 'match' => 'Reverts r123', 'prefix' => 'Reverts', 'infix' => '', 'monograms' => array('r123'), 'suffix' => '', 'offset' => 0, ), ), "Backs out commit\n99\n100" => array( array( 'match' => "Backs out commit\n99\n100", 'prefix' => 'Backs out', 'infix' => 'commit', 'monograms' => array('99', '100'), 'suffix' => '', 'offset' => 0, ), ), + // This tests a degenerate regex behavior, see T9268. + 'Reverts aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz' => array(), + "This doesn't revert anything" => array(), 'nonrevert of r11' => array(), 'fixed a bug' => array(), ); foreach ($map as $input => $expect) { $parser = new DifferentialCustomFieldRevertsParser(); $output = $parser->parseCorpus($input); $this->assertEqual($expect, $output, $input); } } } diff --git a/src/infrastructure/customfield/parser/PhabricatorCustomFieldMonogramParser.php b/src/infrastructure/customfield/parser/PhabricatorCustomFieldMonogramParser.php index 48e922dc62..9c6f33a855 100644 --- a/src/infrastructure/customfield/parser/PhabricatorCustomFieldMonogramParser.php +++ b/src/infrastructure/customfield/parser/PhabricatorCustomFieldMonogramParser.php @@ -1,78 +1,78 @@ getPrefixes(); $suffixes = $this->getSuffixes(); $infixes = $this->getInfixes(); $prefix_regex = $this->buildRegex($prefixes); $infix_regex = $this->buildRegex($infixes, true); $suffix_regex = $this->buildRegex($suffixes, true, true); $monogram_pattern = $this->getMonogramPattern(); $pattern = '/'. '(?:^|\b)'. $prefix_regex. $infix_regex. - '((?:'.$monogram_pattern.'[,\s]*)+)'. - '(?:\band\s+('.$monogram_pattern.'))?'. + '((?:'.$monogram_pattern.'(?:\b|$)[,\s]*)+)'. + '(?:\band\s+('.$monogram_pattern.'(?:\b|$)))?'. $suffix_regex. '(?:$|\b)'. '/'; $matches = null; $ok = preg_match_all( $pattern, $corpus, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); if ($ok === false) { throw new Exception(pht('Regular expression "%s" is invalid!', $pattern)); } $results = array(); foreach ($matches as $set) { $monograms = array_filter(preg_split('/[,\s]+/', $set[3][0])); if (isset($set[4]) && $set[4][0]) { $monograms[] = $set[4][0]; } $results[] = array( 'match' => $set[0][0], 'prefix' => $set[1][0], 'infix' => $set[2][0], 'monograms' => $monograms, 'suffix' => idx(idx($set, 5, array()), 0, ''), 'offset' => $set[0][1], ); } return $results; } private function buildRegex(array $list, $optional = false, $final = false) { $parts = array(); foreach ($list as $string) { $parts[] = preg_quote($string, '/'); } $parts = implode('|', $parts); $maybe_tail = $final ? '' : '\s+'; $maybe_optional = $optional ? '?' : ''; return '(?i:('.$parts.')'.$maybe_tail.')'.$maybe_optional; } }