2424use PHPStan \Type \UnionType ;
2525use function array_key_exists ;
2626use function array_values ;
27- use function chr ;
2827use function count ;
2928use function in_array ;
3029use function is_int ;
31- use function ord ;
3230use function preg_replace ;
3331use function rtrim ;
3432use function sscanf ;
@@ -124,7 +122,6 @@ public function parseGroups(string $regex): ?RegexAstWalkResult
124122 false ,
125123 $ modifiers ,
126124 RegexGroupWalkResult::createEmpty (),
127- false ,
128125 );
129126
130127 if (!$ subjectAsGroupResult ->mightContainEmptyStringLiteral () && !$ this ->containsEscapeK ($ ast )) {
@@ -412,7 +409,6 @@ private function createGroupType(TreeNode $group, bool $maybeConstant, string $p
412409 false ,
413410 $ patternModifiers ,
414411 RegexGroupWalkResult::createEmpty (),
415- false ,
416412 );
417413
418414 if ($ maybeConstant && $ walkResult ->getOnlyLiterals () !== null && $ walkResult ->getOnlyLiterals () !== []) {
@@ -438,15 +434,6 @@ private function createGroupType(TreeNode $group, bool $maybeConstant, string $p
438434 return new UnionType ([new ConstantStringType ('' ), $ result ]);
439435 }
440436 return $ result ;
441- } elseif ($ walkResult ->isNonDecimalInteger ()->yes ()) {
442- $ accessories = [new StringType (), new AccessoryDecimalIntegerStringType (true )];
443- if ($ walkResult ->isNonFalsy ()->yes ()) {
444- $ accessories [] = new AccessoryNonFalsyStringType ();
445- } elseif ($ walkResult ->isNonEmpty ()->yes ()) {
446- $ accessories [] = new AccessoryNonEmptyStringType ();
447- }
448-
449- return new IntersectionType ($ accessories );
450437 } elseif ($ walkResult ->isNonFalsy ()->yes ()) {
451438 return new IntersectionType ([new StringType (), new AccessoryNonFalsyStringType ()]);
452439 } elseif ($ walkResult ->isNonEmpty ()->yes ()) {
@@ -483,7 +470,6 @@ private function walkGroupAst(
483470 bool $ inClass ,
484471 string $ patternModifiers ,
485472 RegexGroupWalkResult $ walkResult ,
486- bool $ inNegativeClass ,
487473 ): RegexGroupWalkResult
488474 {
489475 $ children = $ ast ->getChildren ();
@@ -555,27 +541,19 @@ private function walkGroupAst(
555541 $ walkResult = $ walkResult ->onlyLiterals ($ onlyLiterals );
556542
557543 if ($ literalValue !== null ) {
558- if (!$ inNegativeClass ) {
559- if (Strings::match ($ literalValue , '/^\d+$/ ' ) !== null ) {
560- if ($ walkResult ->isDecimalInteger ()->maybe ()) {
561- $ walkResult = $ walkResult ->decimalInteger (TrinaryLogic::createYes ());
562- }
563- } elseif (
564- $ literalValue === '- '
565- && $ walkResult ->isDecimalInteger ()->maybe ()
566- && !$ walkResult ->hasSeenDecimalIntegerSign ()
567- ) {
568- // a single leading minus sign keeps the string a decimal integer (e.g. "-1")
569- $ walkResult = $ walkResult ->seenDecimalIntegerSign (true );
570- } else {
571- $ walkResult = $ walkResult ->decimalInteger (TrinaryLogic::createNo ());
572- }
573-
574- // a literal token outside a negative class might be (part of) a decimal integer,
575- // so we can no longer guarantee the absence of one
576- if ($ literalValue !== '' ) {
577- $ walkResult = $ walkResult ->nonDecimalInteger (TrinaryLogic::createNo ());
544+ if (Strings::match ($ literalValue , '/^\d+$/ ' ) !== null ) {
545+ if ($ walkResult ->isDecimalInteger ()->maybe ()) {
546+ $ walkResult = $ walkResult ->decimalInteger (TrinaryLogic::createYes ());
578547 }
548+ } elseif (
549+ $ literalValue === '- '
550+ && $ walkResult ->isDecimalInteger ()->maybe ()
551+ && !$ walkResult ->hasSeenDecimalIntegerSign ()
552+ ) {
553+ // a single leading minus sign keeps the string a decimal integer (e.g. "-1")
554+ $ walkResult = $ walkResult ->seenDecimalIntegerSign (true );
555+ } else {
556+ $ walkResult = $ walkResult ->decimalInteger (TrinaryLogic::createNo ());
579557 }
580558
581559 if (!$ walkResult ->isInOptionalQuantification () && $ literalValue !== '' ) {
@@ -595,7 +573,6 @@ private function walkGroupAst(
595573 $ nonEmpty = TrinaryLogic::createYes ();
596574 $ nonFalsy = TrinaryLogic::createYes ();
597575 $ decimalInteger = TrinaryLogic::createYes ();
598- $ nonDecimalInteger = TrinaryLogic::createYes ();
599576 foreach ($ children as $ child ) {
600577 $ childResult = $ this ->walkGroupAst (
601578 $ child ,
@@ -605,15 +582,12 @@ private function walkGroupAst(
605582 ->nonEmpty (TrinaryLogic::createMaybe ())
606583 ->nonFalsy (TrinaryLogic::createMaybe ())
607584 ->decimalInteger (TrinaryLogic::createMaybe ())
608- ->nonDecimalInteger (TrinaryLogic::createMaybe ())
609585 ->seenDecimalIntegerSign (false ),
610- $ inNegativeClass ,
611586 );
612587
613588 $ nonEmpty = $ nonEmpty ->and ($ childResult ->isNonEmpty ());
614589 $ nonFalsy = $ nonFalsy ->and ($ childResult ->isNonFalsy ());
615590 $ decimalInteger = $ decimalInteger ->and ($ childResult ->isDecimalInteger ());
616- $ nonDecimalInteger = $ nonDecimalInteger ->and ($ childResult ->isNonDecimalInteger ());
617591
618592 if ($ newLiterals === null ) {
619593 continue ;
@@ -632,23 +606,14 @@ private function walkGroupAst(
632606 ->onlyLiterals ($ newLiterals )
633607 ->nonEmpty ($ walkResult ->isNonEmpty ()->or ($ nonEmpty ))
634608 ->nonFalsy ($ walkResult ->isNonFalsy ()->or ($ nonFalsy ))
635- ->decimalInteger ($ walkResult ->isDecimalInteger ()->and ($ decimalInteger ))
636- ->nonDecimalInteger ($ walkResult ->isNonDecimalInteger ()->and ($ nonDecimalInteger ));
609+ ->decimalInteger ($ walkResult ->isDecimalInteger ()->and ($ decimalInteger ));
637610 }
638611
639- // a negative class never matches a decimal integer on its own; when it excludes every
640- // digit it can only match non-digit characters, so the result is a non-decimal-int-string
612+ // [^0-9] should not parse as decimal-int-string, and [^list-everything-but-numbers] is technically
613+ // doable but really silly compared to just \d so we can safely assume the string is not a decimal
614+ // integer for negative classes
641615 if ($ ast ->getId () === '#negativeclass ' ) {
642616 $ walkResult = $ walkResult ->decimalInteger (TrinaryLogic::createNo ());
643- if ($ this ->negatedClassExcludesAllDigits ($ ast )) {
644- if ($ walkResult ->isNonDecimalInteger ()->maybe ()) {
645- $ walkResult = $ walkResult ->nonDecimalInteger (TrinaryLogic::createYes ());
646- }
647- } else {
648- $ walkResult = $ walkResult ->nonDecimalInteger (TrinaryLogic::createNo ());
649- }
650-
651- $ inNegativeClass = true ;
652617 }
653618
654619 foreach ($ children as $ child ) {
@@ -657,50 +622,12 @@ private function walkGroupAst(
657622 $ inClass ,
658623 $ patternModifiers ,
659624 $ walkResult ,
660- $ inNegativeClass ,
661625 );
662626 }
663627
664628 return $ walkResult ;
665629 }
666630
667- private function negatedClassExcludesAllDigits (TreeNode $ negativeClass ): bool
668- {
669- $ excludedDigits = [];
670- foreach ($ negativeClass ->getChildren () as $ child ) {
671- if ($ child ->getId () === '#range ' ) {
672- $ from = $ child ->getChild (0 )->getValueValue ();
673- $ to = $ child ->getChild (1 )->getValueValue ();
674- if (strlen ($ from ) === 1 && strlen ($ to ) === 1 ) {
675- for ($ ord = ord ($ from ); $ ord <= ord ($ to ); $ ord ++) {
676- $ char = chr ($ ord );
677- if (Strings::match ($ char , '/^\d$/ ' ) === null ) {
678- continue ;
679- }
680- $ excludedDigits [$ char ] = true ;
681- }
682- }
683- } elseif ($ child ->getId () === 'token ' ) {
684- $ value = $ child ->getValueValue ();
685- if ($ child ->getValueToken () === 'character_type ' && $ value === '\d ' ) {
686- for ($ digit = 0 ; $ digit <= 9 ; $ digit ++) {
687- $ excludedDigits [(string ) $ digit ] = true ;
688- }
689- } elseif (Strings::match ($ value , '/^\d$/ ' ) !== null ) {
690- $ excludedDigits [$ value ] = true ;
691- }
692- }
693- }
694-
695- for ($ digit = 0 ; $ digit <= 9 ; $ digit ++) {
696- if (!array_key_exists ((string ) $ digit , $ excludedDigits )) {
697- return false ;
698- }
699- }
700-
701- return true ;
702- }
703-
704631 private function isMaybeEmptyNode (TreeNode $ node , string $ patternModifiers , bool &$ isNonFalsy ): bool
705632 {
706633 if ($ node ->getId () === '#quantification ' ) {
0 commit comments