#252356 Correct NullPointerException for import statement ending with a dot
authorJulien Enselme <jenselme@netbeans.org>
Fri, 14 Aug 2015 20:21:17 +0200
changeset 18298047218e7b04f
parent 18297 dcf70135c86f
child 18300 8fec1d668f4a
child 18301 17413f763c78
#252356 Correct NullPointerException for import statement ending with a dot

- Report the syntax error
- Don't allow completion
python.editor/src/org/netbeans/modules/python/editor/PythonParser.java
     1.1 --- a/python.editor/src/org/netbeans/modules/python/editor/PythonParser.java	Wed Aug 12 23:27:34 2015 -0500
     1.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/PythonParser.java	Fri Aug 14 20:21:17 2015 +0200
     1.3 @@ -327,6 +327,12 @@
     1.4              tokens.discardOffChannelTokens(true);
     1.5              PythonTokenSource indentedSource = new PythonTokenSource(tokens, fileName);
     1.6              CommonTokenStream indentedTokens = new CommonTokenStream(indentedSource);
     1.7 +            // Import line ending with a dot raise a NullPointerException in
     1.8 +            // org.python.antlr.GrammarActions.makeDottedText called from parser.file_input
     1.9 +            // sanitizeImportTokens will remove the dot token from the list of tokens in
    1.10 +            // indentedTokens to avoid the bug and add an error at this file.
    1.11 +            // See https://netbeans.org/bugzilla/show_bug.cgi?id=252356
    1.12 +            sanitizeImportTokens(indentedTokens, errors, file);
    1.13              org.python.antlr.PythonParser parser;
    1.14              if (charset != null) {
    1.15                  parser = new org.python.antlr.PythonParser(indentedTokens, charset);
    1.16 @@ -391,6 +397,51 @@
    1.17          }
    1.18      }
    1.19  
    1.20 +    private void sanitizeImportTokens(CommonTokenStream indentedTokens, List errors, FileObject file) {
    1.21 +        List tokens = indentedTokens.getTokens();
    1.22 +        List<CommonToken> tokensToRemove = new ArrayList<>();
    1.23 +        int i = 0;
    1.24 +        while (i < tokens.size()) {
    1.25 +            CommonToken importToken = (CommonToken)tokens.get(i);
    1.26 +            if ("import".equals(importToken.getText()) || "from".equals(importToken.getText())) {
    1.27 +                // sanitizeDotTokens return the index of the token that starts the next line
    1.28 +                i = sanitizeDotTokens(tokens, tokensToRemove, importToken, i + 1, errors, file);
    1.29 +            } else {
    1.30 +                i++;
    1.31 +            }
    1.32 +        }
    1.33 +
    1.34 +        for (CommonToken token : tokensToRemove) {
    1.35 +            tokens.remove(token);
    1.36 +        }
    1.37 +    }
    1.38 +
    1.39 +    private int sanitizeDotTokens(List tokens, List tokensToRemove, CommonToken importToken,
    1.40 +            int startIndex, List errors, FileObject file) {
    1.41 +        for (int j = startIndex; j < tokens.size() - 1; j++) {
    1.42 +            CommonToken dotToken = (CommonToken)tokens.get(j);
    1.43 +            CommonToken nextToken = (CommonToken)tokens.get(j + 1);
    1.44 +            if (".".equals(dotToken.getText())) {
    1.45 +                if (nextToken.getText().startsWith("\n")) {
    1.46 +                    tokensToRemove.add(dotToken);
    1.47 +                    String rawTokenText;
    1.48 +                    if (nextToken.getText().startsWith("\n")) {
    1.49 +                        rawTokenText = "\\n";
    1.50 +                    } else {
    1.51 +                        rawTokenText = " ";
    1.52 +                    }
    1.53 +                    errors.add(
    1.54 +                        new DefaultError(null, "Mismatch input '.' expecting NAME\nMissing NAME at '" + rawTokenText + "'",
    1.55 +                            null, file, importToken.getStartIndex(), dotToken.getStopIndex(), Severity.ERROR));
    1.56 +                }
    1.57 +            } else if ("\n".equals(nextToken.getText())) { // End of line, must continue looping from external loop
    1.58 +                return j + 1;
    1.59 +            }
    1.60 +        }
    1.61 +
    1.62 +        return startIndex;
    1.63 +    }
    1.64 +
    1.65      private static String asString(CharSequence sequence) {
    1.66          if (sequence instanceof String) {
    1.67              return (String)sequence;