jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/325963 )

Change subject: Add script to support per-line @suppress annotations in Phan
......................................................................


Add script to support per-line @suppress annotations in Phan

Adds the functionality of per-line @suppress annotations. Phan already
supports per-class or per-method annotations, but does not have any
per-line support due to the PHP AST only returning comments that are
class/property/method level docblocks.

This is a bit of a hack, but get's the job done. Removes the
PhanTypeInvalidLeftOperand issue from blacklist and suppresses it
to demonstrate the supression works as expected.

Change-Id: I5066b3b431fb69175a711ee366e95f31c7c47639
---
M languages/Language.php
M tests/phan/bin/phan
A tests/phan/bin/postprocess-phan.php
M tests/phan/config.php
4 files changed, 151 insertions(+), 3 deletions(-)

Approvals:
  Legoktm: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/languages/Language.php b/languages/Language.php
index 2e4ef89..3eb6546 100644
--- a/languages/Language.php
+++ b/languages/Language.php
@@ -466,6 +466,7 @@
                        $this->namespaceNames = self::$dataCache->getItem( 
$this->mCode, 'namespaceNames' );
                        $validNamespaces = 
MWNamespace::getCanonicalNamespaces();
 
+                       /** @suppress PhanTypeInvalidLeftOperand */
                        $this->namespaceNames = $wgExtraNamespaces + 
$this->namespaceNames + $validNamespaces;
 
                        $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
diff --git a/tests/phan/bin/phan b/tests/phan/bin/phan
index cfaf9a1..bed8c69 100755
--- a/tests/phan/bin/phan
+++ b/tests/phan/bin/phan
@@ -30,10 +30,13 @@
 php7.0 $PHAN \
        --project-root-directory "$ROOT" \
        --config-file "$ROOT/tests/phan/config.php" \
-       --output "$ROOT/tests/phan/issues/issues-${REV}" \
+       --output "$RUN" \
        "${@}"
 
+
+cat "${RUN}" | php "$ROOT/tests/phan/bin/postprocess-phan.php" "${@}" > 
/tmp/phan.$$
 EXIT_CODE="$?"
+mv /tmp/phan.$$ "${RUN}"
 
 # Re-link the latest file
 rm -f "${ISSUES}/latest"
diff --git a/tests/phan/bin/postprocess-phan.php 
b/tests/phan/bin/postprocess-phan.php
new file mode 100644
index 0000000..3e80598
--- /dev/null
+++ b/tests/phan/bin/postprocess-phan.php
@@ -0,0 +1,146 @@
+<?php
+
+abstract class Suppressor {
+       /**
+        * @param string $input
+        * @return bool do errors remain
+        */
+       abstract public function suppress( $input );
+
+       /**
+        * @param string[] $source
+        * @param string $type
+        * @param int $lineno
+        * @return bool
+        */
+       protected function isSuppressed( array $source, $type, $lineno ) {
+               return $lineno > 0 && preg_match(
+                       "|/\*\* @suppress {$type} |",
+                       $source[$lineno - 1]
+               );
+       }
+}
+
+class TextSuppressor extends Suppressor {
+       /**
+        * @param string $input
+        * @return bool do errors remain
+        */
+       public function suppress( $input ) {
+               $hasErrors = false;
+               $errors = [];
+               foreach ( explode( "\n", $input ) as $error ) {
+                       if ( empty( $error ) ) {
+                               continue;
+                       }
+                       if ( !preg_match( '/^(.*):(\d+) (Phan\w+) (.*)$/', 
$error, $matches ) ) {
+                               echo "Failed to parse line: $error\n";
+                               continue;
+                       }
+                       list( $source, $file, $lineno, $type, $message ) = 
$matches;
+                       $errors[$file][] = [
+                               'orig' => $error,
+                               // convert from 1 indexed to 0 indexed
+                               'lineno' => $lineno - 1,
+                               'type' => $type,
+                       ];
+               }
+               foreach ( $errors  as $file => $fileErrors ) {
+                       $source = file( $file );
+                       foreach ( $fileErrors as $error ) {
+                               if ( !$this->isSuppressed( $source, 
$error['type'], $error['lineno'] ) ) {
+                                       echo $error['orig'], "\n";
+                                       $hasErrors = true;
+                               }
+                       }
+               }
+
+               return $hasErrors;
+       }
+}
+
+class CheckStyleSuppressor extends Suppressor {
+       /**
+        * @param string $input
+        * @return bool True do errors remain
+        */
+       public function suppress( $input ) {
+               $dom = new DOMDocument();
+               $dom->loadXML( $input );
+               $hasErrors = false;
+               // DOMNodeList's are "live", convert to an array so it works as 
expected
+               $files = [];
+               foreach ( $dom->getElementsByTagName( 'file' ) as $file ) {
+                       $files[] = $file;
+               }
+               foreach ( $files as $file ) {
+                       $errors = [];
+                       foreach ( $file->getElementsByTagName( 'error' ) as 
$error ) {
+                               $errors[] = $error;
+                       }
+                       $source = file( $file->getAttribute( 'name' ) );
+                       $fileHasErrors = false;
+                       foreach ( $errors as $error ) {
+                               $lineno = $error->getAttribute( 'line' ) - 1;
+                               $type = $error->getAttribute( 'source' );
+                               if ( $this->isSuppressed( $source, $type, 
$lineno ) ) {
+                                       $error->parentNode->removeChild( $error 
);
+                               } else {
+                                       $fileHasErrors = true;
+                                       $hasErrors = true;
+                               }
+                       }
+                       if ( !$fileHasErrors ) {
+                               $file->parentNode->removeChild( $file );
+                       }
+               }
+               echo $dom->saveXML();
+
+               return $hasErrors;
+       }
+}
+
+class NoopSuppressor extends Suppressor {
+       private $mode;
+
+       public function __construct( $mode ) {
+               $this->mode = $mode;
+       }
+       public function suppress( $input ) {
+               echo "Unsupported output mode: {$this->mode}\n$input";
+               return true;
+       }
+}
+
+$opt = getopt( "m:", [ "output-mode:" ] );
+// if provided multiple times getopt returns an array
+if ( isset( $opt['m'] ) ) {
+       $mode = $opt['m'];
+} elseif ( isset( $mode['output-mode'] ) ) {
+       $mode = $opt['output-mode'];
+} else {
+       $mode = 'text';
+}
+if ( is_array( $mode ) ) {
+       // If an option is passed multiple times getopt returns an
+       // array. Just take the last one.
+       $mode = end( $mode );
+}
+
+switch ( $mode ) {
+case 'text':
+       $suppressor = new TextSuppressor();
+       break;
+case 'checkstyle':
+       $suppressor = new CheckStyleSuppressor();
+       break;
+default:
+       $suppressor = new NoopSuppressor( $mode );
+}
+
+$input = file_get_contents( 'php://stdin' );
+$hasErrors = $suppressor->suppress( $input );
+
+if ( $hasErrors ) {
+       exit( 1 );
+}
diff --git a/tests/phan/config.php b/tests/phan/config.php
index 1f14143..7dcc5c4 100644
--- a/tests/phan/config.php
+++ b/tests/phan/config.php
@@ -323,8 +323,6 @@
                "PhanTypeArraySuspicious",
                // approximate error count: 4
                "PhanTypeComparisonFromArray",
-               // approximate error count: 1
-               "PhanTypeInvalidLeftOperand",
                // approximate error count: 3
                "PhanTypeInvalidRightOperand",
                // approximate error count: 563

-- 
To view, visit https://gerrit.wikimedia.org/r/325963
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I5066b3b431fb69175a711ee366e95f31c7c47639
Gerrit-PatchSet: 9
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <ebernhard...@wikimedia.org>
Gerrit-Reviewer: EBernhardson <ebernhard...@wikimedia.org>
Gerrit-Reviewer: Legoktm <lego...@member.fsf.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to