Browse Source

bug: consider function modifiers for `statement_indentation` (#6978)

Tom Sullivan 1 year ago
parent
commit
75efcd735c

+ 57 - 1
src/Fixer/Whitespace/StatementIndentationFixer.php

@@ -92,7 +92,6 @@ else {
             T_CASE,
             T_DEFAULT,
             T_TRY,
-            T_FUNCTION,
             T_CLASS,
             T_INTERFACE,
             T_TRAIT,
@@ -119,6 +118,20 @@ else {
             $this->extractIndent($this->computeNewLineContent($tokens, 0)),
         );
 
+        $methodModifierTokens = [
+            //  https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#grammar-visibility-modifier
+            T_PUBLIC,
+            T_PROTECTED,
+            T_PRIVATE,
+            //  https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#grammar-static-modifier
+            T_STATIC,
+            //  https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#grammar-class-modifier
+            T_ABSTRACT,
+            T_FINAL,
+        ];
+
+        $methodModifierIndents = [];
+
         /**
          * @var list<array{
          *     type: 'block'|'block_signature'|'statement',
@@ -249,6 +262,49 @@ else {
                 continue;
             }
 
+            if ($token->isGivenKind($methodModifierTokens)) {
+                $methodModifierIndents[$index] = $lastIndent;
+
+                continue;
+            }
+
+            if ($token->isGivenKind(T_FUNCTION)) {
+                $x = $tokens->getPrevMeaningfulToken($index);
+                while (
+                    null !== $x
+                    && $tokens[$x]->isGivenKind($methodModifierTokens)
+                    && \array_key_exists($x, $methodModifierIndents)
+                ) {
+                    $lastIndent = $methodModifierIndents[$x];
+                    $x = $tokens->getPrevMeaningfulToken($x);
+                }
+
+                $methodModifierIndents = [];
+
+                for ($endIndex = $index + 1, $max = \count($tokens); $endIndex < $max; ++$endIndex) {
+                    if ($tokens[$endIndex]->equals('(')) {
+                        $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endIndex);
+
+                        continue;
+                    }
+
+                    if ($tokens[$endIndex]->equalsAny(['{', ';'])) {
+                        break;
+                    }
+                }
+
+                $scopes[] = [
+                    'type' => 'block_signature',
+                    'skip' => false,
+                    'end_index' => $endIndex,
+                    'end_index_inclusive' => true,
+                    'initial_indent' => $this->getLineIndentationWithBracesCompatibility($tokens, $index, $lastIndent),
+                    'is_indented_block' => $token->isGivenKind([T_EXTENDS, T_IMPLEMENTS]),
+                ];
+
+                continue;
+            }
+
             if (
                 $token->isWhitespace()
                 || ($index > 0 && $tokens[$index - 1]->isGivenKind(T_OPEN_TAG))

+ 123 - 0
tests/Fixer/Whitespace/StatementIndentationFixerTest.php

@@ -185,6 +185,129 @@ class Foo {
  }',
         ];
 
+        yield 'multiple class methods with many permutations of visibility modifiers' => [
+            '<?php
+abstract class Test {
+    final protected function test_final_protected() {}
+    static private function test_static_private() {}
+    private function test_private() {}
+    private static function test_private_static() {}
+    abstract public static function test_abstract_public_static();
+    abstract static public function test_abstract_static_public();
+    abstract public function test_abstract_public();
+    protected abstract function test_protected_abstract();
+    public abstract function test_public_abstract();
+    final static protected function test_final_static_protected() {}
+    final private static function test_final_private_static() {}
+    public final function test_public_final() {}
+    final private function test_final_private() {}
+    static final public function test_static_final_public() {}
+    protected abstract static function test_protected_abstract_static();
+    public static abstract function test_public_static_abstract();
+    protected static abstract function test_protected_static_abstract();
+    static final function test_static_final() {}
+    final static private function test_final_static_private() {}
+    static protected abstract function test_static_protected_abstract();
+    public abstract static function test_public_abstract_static();
+    static final protected function test_static_final_protected() {}
+    final public static function test_final_public_static() {}
+    static final private function test_static_final_private() {}
+    abstract protected function test_abstract_protected();
+    abstract static protected function test_abstract_static_protected();
+    private static final function test_private_static_final() {}
+    final static function test_final_static() {}
+    protected static function test_protected_static() {}
+    protected function test_protected() {}
+    public static function test_public_static() {}
+    final function test_final() {}
+    abstract protected static function test_abstract_protected_static();
+    static protected function test_static_protected() {}
+    static abstract function test_static_abstract();
+    static abstract protected function test_static_abstract_protected();
+    protected final static function test_protected_final_static() {}
+    static public final function test_static_public_final() {}
+    public final static function test_public_final_static() {}
+    abstract static function test_abstract_static();
+    public static final function test_public_static_final() {}
+    static function test_static() {}
+    abstract function test_abstract();
+    static protected final function test_static_protected_final() {}
+    static private final function test_static_private_final() {}
+    private final function test_private_final() {}
+    static public abstract function test_static_public_abstract();
+    protected static final function test_protected_static_final() {}
+    final protected static function test_final_protected_static() {}
+    final static public function test_final_static_public() {}
+    static public function test_static_public() {}
+    function test_() {}
+    static abstract public function test_static_abstract_public();
+    final public function test_final_public() {}
+    private final static function test_private_final_static() {}
+    protected final function test_protected_final() {}
+    public function test_public() {}
+}',
+            '<?php
+abstract class Test {
+                      final protected function test_final_protected() {}
+                 static private function test_static_private() {}
+                    private function test_private() {}
+             private static function test_private_static() {}
+        abstract public static function test_abstract_public_static();
+                 abstract static public function test_abstract_static_public();
+abstract public function test_abstract_public();
+protected abstract function test_protected_abstract();
+       public abstract function test_public_abstract();
+       final static protected function test_final_static_protected() {}
+                     final private static function test_final_private_static() {}
+           public final function test_public_final() {}
+                      final private function test_final_private() {}
+            static final public function test_static_final_public() {}
+           protected abstract static function test_protected_abstract_static();
+                 public static abstract function test_public_static_abstract();
+                       protected static abstract function test_protected_static_abstract();
+                      static final function test_static_final() {}
+                final static private function test_final_static_private() {}
+             static protected abstract function test_static_protected_abstract();
+ public abstract static function test_public_abstract_static();
+     static final protected function test_static_final_protected() {}
+      final public static function test_final_public_static() {}
+     static final private function test_static_final_private() {}
+  abstract protected function test_abstract_protected();
+      abstract static protected function test_abstract_static_protected();
+                    private static final function test_private_static_final() {}
+               final static function test_final_static() {}
+           protected static function test_protected_static() {}
+        protected function test_protected() {}
+   public static function test_public_static() {}
+         final function test_final() {}
+                   abstract protected static function test_abstract_protected_static();
+     static protected function test_static_protected() {}
+      static abstract function test_static_abstract();
+        static abstract protected function test_static_abstract_protected();
+               protected final static function test_protected_final_static() {}
+static public final function test_static_public_final() {}
+       public final static function test_public_final_static() {}
+                    abstract static function test_abstract_static();
+                    public static final function test_public_static_final() {}
+   static function test_static() {}
+          abstract function test_abstract();
+                      static protected final function test_static_protected_final() {}
+                       static private final function test_static_private_final() {}
+        private final function test_private_final() {}
+                  static public abstract function test_static_public_abstract();
+                     protected static final function test_protected_static_final() {}
+                  final protected static function test_final_protected_static() {}
+               final static public function test_final_static_public() {}
+                  static public function test_static_public() {}
+                    function test_() {}
+                       static abstract public function test_static_abstract_public();
+          final public function test_final_public() {}
+                 private final static function test_private_final_static() {}
+                protected final function test_protected_final() {}
+  public function test_public() {}
+}',
+        ];
+
         yield 'trait method definition arguments' => [
             '<?php
 trait Foo {