You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ju...@apache.org on 2019/10/31 13:04:04 UTC

[netbeans] branch master updated: [NETBEANS-3002] NullPointerExceptions during PHPStan inspection (#1592)

This is an automated email from the ASF dual-hosted git repository.

junichi11 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new f6fc0fc  [NETBEANS-3002] NullPointerExceptions during PHPStan inspection (#1592)
f6fc0fc is described below

commit f6fc0fcff68ad47e111aa6dcc62d8e315913cf78
Author: Junichi Yamamoto <ju...@apache.org>
AuthorDate: Thu Oct 31 22:03:57 2019 +0900

    [NETBEANS-3002] NullPointerExceptions during PHPStan inspection (#1592)
    
    - The file name value can be null
    
    e.g.
    ```
    <file>
      <error severity="error" message="Ignored error pattern #TEST# was not matched in reported errors." />
    </file>
    ```
    So, in this case, just ignore the error.
    
    - There is a difference in file name value between the old version and the new version
    
    e.g. Inspect the following app directory (myproject is the working directory)
    - myproject/app/Test1.php
    - myproject/app/Test2.php
    
    old version: the relative path from target directory
    ```
    <file name="Test1.php">
      <error line="13" column="1" severity="error" message="Binary operation &quot;+&quot; between string and 2 results in an error." />
    </file>
    <file name="Test2.php">
      <error line="9" column="1" severity="error" message="Anonymous function should return string but returns void." />
      <error line="9" column="1" severity="error" message="Result of closure (void) is used." />
    </file>
    ```
    
    new verison: the relative path from working directory
    ```
    <file name="app/Test1.php">
      <error line="13" column="1" severity="error" message="Binary operation &quot;+&quot; between string and 2 results in an error." />
    </file>
    <file name="app/Test2.php">
      <error line="9" column="1" severity="error" message="Anonymous function should return string but returns void." />
      <error line="9" column="1" severity="error" message="Result of closure (void) is used." />
    </file>
    ```
    
    So, check also the relative path from the working directory.
---
 .../modules/php/analysis/commands/PHPStan.java     | 23 +++++----
 .../php/analysis/parsers/PHPStanReportParser.java  | 54 ++++++++++++++++------
 .../phpstan/PHPStanSupport/netbeans3022/Test1.php  |  3 ++
 .../phpstan/PHPStanSupport/netbeans3022/Test2.php  |  3 ++
 .../data/phpstan/phpstan-log-netbeans-3022-win.xml | 33 +++++++++++++
 .../phpstan-log-netbeans-3022-without-workdir.xml  | 30 ++++++++++++
 .../data/phpstan/phpstan-log-netbeans-3022.xml     | 33 +++++++++++++
 .../analysis/parsers/PHPStanReportParserTest.java  | 36 +++++++++++++--
 8 files changed, 189 insertions(+), 26 deletions(-)

diff --git a/php/php.code.analysis/src/org/netbeans/modules/php/analysis/commands/PHPStan.java b/php/php.code.analysis/src/org/netbeans/modules/php/analysis/commands/PHPStan.java
index 3ee2d30..997a561 100644
--- a/php/php.code.analysis/src/org/netbeans/modules/php/analysis/commands/PHPStan.java
+++ b/php/php.code.analysis/src/org/netbeans/modules/php/analysis/commands/PHPStan.java
@@ -102,19 +102,21 @@ public final class PHPStan {
 
     @NbBundle.Messages({
         "# {0} - counter",
-        "PHPStan.analyze=PHPStan (analyze #{0})",})
+        "PHPStan.analyze=PHPStan (analyze #{0})"
+    })
     @CheckForNull
     public List<Result> analyze(PHPStanParams params, FileObject file) {
         assert file.isValid() : "Invalid file given: " + file;
         try {
-            Integer result = getExecutable(Bundle.PHPStan_analyze(analyzeGroupCounter++), findWorkDir(file))
+            FileObject workDir = findWorkDir(file);
+            Integer result = getExecutable(Bundle.PHPStan_analyze(analyzeGroupCounter++), workDir == null ? null : FileUtil.toFile(workDir))
                     .additionalParameters(getParameters(params, file))
                     .runAndWait(getDescriptor(), "Running phpstan..."); // NOI18N
             if (result == null) {
                 return null;
             }
 
-            return PHPStanReportParser.parse(XML_LOG, file);
+            return PHPStanReportParser.parse(XML_LOG, file, workDir);
         } catch (CancellationException ex) {
             // cancelled
             return Collections.emptyList();
@@ -133,17 +135,22 @@ public final class PHPStan {
      * @return project directory or {@code null}
      */
     @CheckForNull
-    private File findWorkDir(FileObject file) {
+    private FileObject findWorkDir(FileObject file) {
         assert file != null;
         Project project = FileOwnerQuery.getOwner(file);
+        FileObject workDir = null;
         if (project != null) {
-            File projectDir = FileUtil.toFile(project.getProjectDirectory());
+            workDir = project.getProjectDirectory();
             if (LOGGER.isLoggable(Level.FINE)) {
-                LOGGER.log(Level.FINE, "Project directory for {0} found in {1}", new Object[]{FileUtil.toFile(file), projectDir});
+                if (workDir != null) {
+                    LOGGER.log(Level.FINE, "Project directory for {0} is found in {1}", new Object[]{FileUtil.toFile(file), workDir}); // NOI18N
+                } else {
+                    // the file/directory may not be in a PHP project
+                    LOGGER.log(Level.FINE, "Project directory for {0} is not found", FileUtil.toFile(file)); // NOI18N
+                }
             }
-            return projectDir;
         }
-        return null;
+        return workDir;
     }
 
     private PhpExecutable getExecutable(String title, @NullAllowed File workDir) {
diff --git a/php/php.code.analysis/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParser.java b/php/php.code.analysis/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParser.java
index 8a2937d..2ed5e47 100644
--- a/php/php.code.analysis/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParser.java
+++ b/php/php.code.analysis/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParser.java
@@ -33,6 +33,7 @@ import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.modules.php.analysis.results.Result;
 import org.netbeans.modules.php.api.util.FileUtils;
 import org.openide.filesystems.FileObject;
@@ -56,25 +57,28 @@ public class PHPStanReportParser extends DefaultHandler {
     private Result currentResult = null;
     private String currentFile = null;
     private final FileObject root;
+    @NullAllowed
+    private final FileObject workDir;
 
-    private PHPStanReportParser(FileObject root) throws SAXException {
+    private PHPStanReportParser(FileObject root, @NullAllowed FileObject workDir) throws SAXException {
         this.xmlReader = FileUtils.createXmlReader();
         this.root = root;
+        this.workDir = workDir;
     }
 
-    private static PHPStanReportParser create(Reader reader, FileObject root) throws SAXException, IOException {
-        PHPStanReportParser parser = new PHPStanReportParser(root);
+    private static PHPStanReportParser create(Reader reader, FileObject root, @NullAllowed FileObject workDir) throws SAXException, IOException {
+        PHPStanReportParser parser = new PHPStanReportParser(root, workDir);
         parser.xmlReader.setContentHandler(parser);
         parser.xmlReader.parse(new InputSource(reader));
         return parser;
     }
 
     @CheckForNull
-    public static List<Result> parse(File file, FileObject root) {
+    public static List<Result> parse(File file, FileObject root, @NullAllowed FileObject workDir) {
         try {
             sanitizeFile(file);
             try (Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) { // NOI18N
-                return create(reader, root).getResults();
+                return create(reader, root, workDir).getResults();
             }
         } catch (IOException | SAXException ex) {
             LOGGER.log(Level.INFO, null, ex);
@@ -112,7 +116,7 @@ public class PHPStanReportParser extends DefaultHandler {
     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
         if ("file".equals(qName)) { // NOI18N
             processFileStart(attributes);
-        } else if ("error".equals(qName)) { // NOI18N
+        } else if ("error".equals(qName) && currentFile != null) { // NOI18N
             processResultStart(attributes);
         }
     }
@@ -130,11 +134,16 @@ public class PHPStanReportParser extends DefaultHandler {
         assert currentResult == null : currentResult.getFilePath();
         assert currentFile == null : currentFile;
 
-        currentFile = getCurrentFile(attributes.getValue("name")); // NOI18N
+        // NETBEANS-3022 name value can be null
+        // e.g.
+        // <file>
+        //   <error severity="error" message="Ignored error pattern #TEST# was not matched in reported errors." />
+        // </file>
+        String name = attributes.getValue("name"); // NOI18N
+        currentFile = name == null ? null : getCurrentFile(name);
     }
 
     private void processFileEnd() {
-        assert currentFile != null;
         currentFile = null;
     }
 
@@ -160,8 +169,9 @@ public class PHPStanReportParser extends DefaultHandler {
     }
 
     private void processResultEnd() {
-        assert currentResult != null;
-        results.add(currentResult);
+        if (currentResult != null) {
+            results.add(currentResult);
+        }
         currentResult = null;
     }
 
@@ -178,20 +188,38 @@ public class PHPStanReportParser extends DefaultHandler {
     @CheckForNull
     private String getCurrentFile(String fileName) {
         String sanitizedFileName = sanitizeFileName(fileName);
+        // if working directory is null, the file name is the absolute path
+        // e.g. <file name="/path/to/MyPHPProject/app/Test1.php">
+        File file = new File(sanitizedFileName);
+        if (file.isAbsolute()) {
+            return file.getAbsolutePath();
+        }
+
+        String currentFilePath = sanitizedFileName;
         FileObject rootDirectory = root;
         if (!root.isFolder()) {
             rootDirectory = root.getParent();
         }
         if (rootDirectory.isFolder()) {
+            // windows: <file name="app\index.php">
+            sanitizedFileName = sanitizedFileName.replace('\\', '/');
             FileObject current = rootDirectory.getFileObject(sanitizedFileName);
             if (current == null) {
+                // NETBANS-3022 try checking relative file path from working directory
+                if (workDir != null) {
+                    rootDirectory = workDir;
+                    current = rootDirectory.getFileObject(sanitizedFileName);
+                }
+            }
+            if (current == null) {
                 LOGGER.log(Level.WARNING, "Cannot get the current file: file name {0}, root directory {1}", // NOI18N
                         new Object[]{fileName, FileUtil.toFile(rootDirectory).getAbsolutePath()});
-                return null;
+                currentFilePath = null;
+            } else {
+                currentFilePath = FileUtil.toFile(current).getAbsolutePath();
             }
-            return FileUtil.toFile(current).getAbsolutePath();
         }
-        return sanitizedFileName;
+        return currentFilePath;
     }
 
     private String sanitizeFileName(String fileName) {
diff --git a/php/php.code.analysis/test/unit/data/phpstan/PHPStanSupport/netbeans3022/Test1.php b/php/php.code.analysis/test/unit/data/phpstan/PHPStanSupport/netbeans3022/Test1.php
new file mode 100644
index 0000000..90c534d
--- /dev/null
+++ b/php/php.code.analysis/test/unit/data/phpstan/PHPStanSupport/netbeans3022/Test1.php
@@ -0,0 +1,3 @@
+<?php
+/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; and to You under the Apache License, Version 2.0. */
+// dummy file
diff --git a/php/php.code.analysis/test/unit/data/phpstan/PHPStanSupport/netbeans3022/Test2.php b/php/php.code.analysis/test/unit/data/phpstan/PHPStanSupport/netbeans3022/Test2.php
new file mode 100644
index 0000000..90c534d
--- /dev/null
+++ b/php/php.code.analysis/test/unit/data/phpstan/PHPStanSupport/netbeans3022/Test2.php
@@ -0,0 +1,3 @@
+<?php
+/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; and to You under the Apache License, Version 2.0. */
+// dummy file
diff --git a/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022-win.xml b/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022-win.xml
new file mode 100644
index 0000000..04185ce
--- /dev/null
+++ b/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022-win.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<checkstyle>
+<file name="netbeans3022\Test1.php">
+  <error line="13" column="1" severity="error" message="Binary operation &quot;+&quot; between string and 2 results in an error." />
+</file>
+<file name="netbeans3022\Test2.php">
+  <error line="9" column="1" severity="error" message="Anonymous function should return string but returns void." />
+  <error line="9" column="1" severity="error" message="Result of closure (void) is used." />
+</file>
+<file>
+  <error severity="error" message="Ignored error pattern #TEST# was not matched in reported errors." />
+</file>
+</checkstyle>
diff --git a/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022-without-workdir.xml b/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022-without-workdir.xml
new file mode 100644
index 0000000..56460df
--- /dev/null
+++ b/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022-without-workdir.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<checkstyle>
+<file name="/home/junichi11/NetBeansProjects/PHPStan/app/Test1.php">
+  <error line="13" column="1" severity="error" message="Binary operation &quot;+&quot; between string and 2 results in an error." />
+</file>
+<file name="/home/junichi11/NetBeansProjects/PHPStan/app/Test2.php">
+  <error line="9" column="1" severity="error" message="Anonymous function should return string but returns void." />
+  <error line="9" column="1" severity="error" message="Result of closure (void) is used." />
+</file>
+</checkstyle>
diff --git a/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022.xml b/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022.xml
new file mode 100644
index 0000000..8c04456
--- /dev/null
+++ b/php/php.code.analysis/test/unit/data/phpstan/phpstan-log-netbeans-3022.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<checkstyle>
+<file name="netbeans3022/Test1.php">
+  <error line="13" column="1" severity="error" message="Binary operation &quot;+&quot; between string and 2 results in an error." />
+</file>
+<file name="netbeans3022/Test2.php">
+  <error line="9" column="1" severity="error" message="Anonymous function should return string but returns void." />
+  <error line="9" column="1" severity="error" message="Result of closure (void) is used." />
+</file>
+<file>
+  <error severity="error" message="Ignored error pattern #TEST# was not matched in reported errors." />
+</file>
+</checkstyle>
diff --git a/php/php.code.analysis/test/unit/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParserTest.java b/php/php.code.analysis/test/unit/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParserTest.java
index cdb74f2..cd41d38 100644
--- a/php/php.code.analysis/test/unit/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParserTest.java
+++ b/php/php.code.analysis/test/unit/src/org/netbeans/modules/php/analysis/parsers/PHPStanReportParserTest.java
@@ -35,8 +35,9 @@ public class PHPStanReportParserTest extends NbTestCase {
     }
 
     public void testParse() throws Exception {
-        FileObject root = getRoot("phpstan/PHPStanSupport");
-        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log.xml"), root);
+        FileObject root = getDataDir("phpstan/PHPStanSupport");
+        FileObject workDir = root;
+        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log.xml"), root, workDir);
         assertNotNull(results);
 
         assertEquals(4, results.size());
@@ -61,12 +62,37 @@ public class PHPStanReportParserTest extends NbTestCase {
     }
 
     public void testParseWithOtherOutput() throws Exception {
-        FileObject root = getRoot("phpstan/PHPStanSupport");
-        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log-with-other-output.xml"), root);
+        FileObject root = getDataDir("phpstan/PHPStanSupport");
+        FileObject workDir = root;
+        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log-with-other-output.xml"), root, workDir);
         assertNotNull(results);
         assertEquals(2, results.size());
     }
 
+    public void testParseNetBeans3022() throws Exception {
+        FileObject root = getDataDir("phpstan/PHPStanSupport/netbeans3022");
+        FileObject workDir = getDataDir("phpstan/PHPStanSupport");
+        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log-netbeans-3022.xml"), root, workDir);
+        assertNotNull(results);
+        assertEquals(3, results.size());
+    }
+
+    public void testParseNetBeans3022Win() throws Exception {
+        FileObject root = getDataDir("phpstan/PHPStanSupport/netbeans3022");
+        FileObject workDir = getDataDir("phpstan/PHPStanSupport");
+        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log-netbeans-3022-win.xml"), root, workDir);
+        assertNotNull(results);
+        assertEquals(3, results.size());
+    }
+
+    public void testParseNetBeans3022WithoutWorkDir() throws Exception {
+        FileObject root = getDataDir("phpstan/PHPStanSupport/netbeans3022");
+        FileObject workDir = null;
+        List<Result> results = PHPStanReportParser.parse(getLogFile("phpstan-log-netbeans-3022-without-workdir.xml"), root, workDir);
+        assertNotNull(results);
+        assertEquals(3, results.size());
+    }
+
     private File getLogFile(String name) throws Exception {
         assertNotNull(name);
         File phpstan = new File(getDataDir(), "phpstan");
@@ -75,7 +101,7 @@ public class PHPStanReportParserTest extends NbTestCase {
         return xmlLog;
     }
 
-    private FileObject getRoot(String name) {
+    private FileObject getDataDir(String name) {
         assertNotNull(name);
         FileObject dataDir = FileUtil.toFileObject(getDataDir());
         return dataDir.getFileObject(name);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists