From b5aac4bbdf605a28c5ea021a3808de2821e73890 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Fri, 16 Mar 2018 14:56:37 -0500 Subject: [PATCH] Normalize behavior between the two parsers - handling for some array types - handling for implements types - checking for script elements #412 --- .../LocalVariableReferenceSearchTests.java | 112 +++--- .../core/tests/basic/GenericsTests.java | 29 +- .../core/tests/basic/GroovySimpleTests.java | 2 +- .../groovy/core/tests/basic/TraitsTests.java | 10 +- .../groovy/antlr/AntlrParserPlugin.java | 49 +-- .../groovy/antlr/AntlrParserPlugin.java | 99 ++---- .../groovy/ast/ClassCodeVisitorSupport.java | 6 +- .../codehaus/groovy/control/SourceUnit.java | 24 +- .../groovy/parser/antlr4/AstBuilder.java | 15 +- .../groovy/antlr/AntlrParserPlugin.java | 329 ++++++++---------- .../groovy/ast/ClassCodeVisitorSupport.java | 6 +- .../codehaus/groovy/control/SourceUnit.java | 24 +- .../jdt/groovy/core/util/GroovyUtils.java | 3 +- 13 files changed, 314 insertions(+), 394 deletions(-) diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/LocalVariableReferenceSearchTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/LocalVariableReferenceSearchTests.java index 980b3ef91c..0ecde7f952 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/LocalVariableReferenceSearchTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/LocalVariableReferenceSearchTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,147 +19,145 @@ public final class LocalVariableReferenceSearchTests extends SearchTestSuite { - private static final String XXX = "xxx"; - @Test - public void testvarReference1() throws Exception { + public void testVarReference1() throws Exception { String contents = "def xxx"; - int nameStart = contents.indexOf(XXX); + int nameStart = contents.indexOf("xxx"); doTestForReferencesInScript(contents, createRegions(nameStart)); } @Test - public void testvarReference2() throws Exception { + public void testVarReference2() throws Exception { String contents = "def xxx\nxxx"; - int nameStart = contents.indexOf(XXX); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx"); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferencesInScript(contents, createRegions(nameStart, nameStart2)); } @Test - public void testvarReference3() throws Exception { + public void testVarReference3() throws Exception { String contents = "def xxx() { \ndef xxx\n xxx\n xxx() }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('(')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); - int nameStart3 = contents.indexOf(XXX, nameStart2+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('(')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); + int nameStart3 = contents.indexOf("xxx", nameStart2 + 1); doTestForReferences(contents, 4, createRegions(nameStart, nameStart2, nameStart3)); } @Test - public void testvarReference4() throws Exception { + public void testVarReference4() throws Exception { String contents = "def xxx(xxx) { \n xxx\n xxx() }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('(')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); - int nameStart3 = contents.indexOf(XXX, nameStart2+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('(')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); + int nameStart3 = contents.indexOf("xxx", nameStart2 + 1); doTestForReferences(contents, 4, createRegions(nameStart, nameStart2, nameStart3)); } @Test - public void testvarReference5() throws Exception { + public void testVarReference5() throws Exception { String contents = "def xxx(int xxx) { \n xxx\n xxx() }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('(')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); - int nameStart3 = contents.indexOf(XXX, nameStart2+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('(')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); + int nameStart3 = contents.indexOf("xxx", nameStart2 + 1); doTestForReferences(contents, 4, createRegions(nameStart, nameStart2, nameStart3)); } @Test - public void testvarReference6() throws Exception { + public void testVarReference6() throws Exception { String contents = "def xxx = {int xxx -> \n xxx\n xxx() }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('{')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); - int nameStart3 = contents.indexOf(XXX, nameStart2+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('{')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); + int nameStart3 = contents.indexOf("xxx", nameStart2 + 1); doTestForReferencesInScript(contents, createRegions(nameStart, nameStart2, nameStart3)); } @Test - public void testvarReference7() throws Exception { + public void testVarReference7() throws Exception { String contents = "def xxx = {int xxx \n xxx\n xxx() }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('{')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); - int nameStart3 = contents.indexOf(XXX, nameStart2+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('{')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); + int nameStart3 = contents.indexOf("xxx", nameStart2 + 1); doTestForReferencesInScript(contents, createRegions(nameStart, nameStart2, nameStart3)); } @Test - public void testvarReference8() throws Exception { + public void testVarReference8() throws Exception { String contents = "for (xxx in 0..7) \n { xxx }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('(')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('(')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferencesInScript(contents, createRegions(nameStart, nameStart2)); } @Test - public void testvarReference9() throws Exception { + public void testVarReference9() throws Exception { String contents = "def x1(xxx) { xxx }\ndef x2(xxx) { xxx }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('}')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('}')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 5, createRegions(nameStart, nameStart2)); } @Test - public void testvarReference10() throws Exception { + public void testVarReference10() throws Exception { String contents = "class First {\n def xxx \ndef x2(xxx) { xxx } }"; - int nameStart = contents.indexOf(XXX, contents.indexOf('(')); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx", contents.indexOf('(')); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 1, createRegions(nameStart, nameStart2)); } @Test - public void testvarReferenceInGString1() throws Exception { + public void testVarReferenceInGString1() throws Exception { String contents = "def xxx\n\"${xxx}\""; - int nameStart = contents.indexOf(XXX); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx"); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 3, createRegions(nameStart, nameStart2)); } @Test - public void testvarReferenceInGString2() throws Exception { + public void testVarReferenceInGString2() throws Exception { String contents = "def xxx\n\"${xxx.toString()}\""; - int nameStart = contents.indexOf(XXX); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx"); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 3, createRegions(nameStart, nameStart2)); } @Test - public void testvarReferenceInGString3() throws Exception { + public void testVarReferenceInGString3() throws Exception { String contents = "def xxx\n\"${blah(xxx)}\""; - int nameStart = contents.indexOf(XXX); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx"); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 3, createRegions(nameStart, nameStart2)); } @Test - public void testvarReferenceInGString4() throws Exception { + public void testVarReferenceInGString4() throws Exception { String contents = "def xxx\n\"${xxx} \""; - int nameStart = contents.indexOf(XXX); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx"); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 3, createRegions(nameStart, nameStart2)); } @Test - public void testvarReferenceInGString5() throws Exception { + public void testVarReferenceInGString5() throws Exception { String contents = "def xxx\n\"${xxx }\""; - int nameStart = contents.indexOf(XXX); - int nameStart2 = contents.indexOf(XXX, nameStart+1); + int nameStart = contents.indexOf("xxx"); + int nameStart2 = contents.indexOf("xxx", nameStart + 1); doTestForReferences(contents, 3, createRegions(nameStart, nameStart2)); } //-------------------------------------------------------------------------- - private MatchRegion[] createRegions(int...nameStarts) { + private MatchRegion[] createRegions(int... nameStarts) { MatchRegion[] regions = new MatchRegion[nameStarts.length]; - for (int i = 0; i < nameStarts.length; i++) { - regions[i] = new MatchRegion(nameStarts[i], XXX.length()); + for (int i = 0, n = nameStarts.length; i < n; i += 1) { + regions[i] = new MatchRegion(nameStarts[i], "xxx".length()); } return regions; } private void doTestForReferencesInScript(String contents, MatchRegion[] matchLocations) throws Exception { - doTestForVarReferences(contents, 3, XXX, matchLocations[0].offset, matchLocations); + doTestForVarReferences(contents, 3, "xxx", matchLocations[0].offset, matchLocations); } private void doTestForReferences(String contents, int locationInParent, MatchRegion[] matchLocations) throws Exception { - doTestForVarReferences(contents, locationInParent, XXX, matchLocations[0].offset, matchLocations); + doTestForVarReferences(contents, locationInParent, "xxx", matchLocations[0].offset, matchLocations); } } diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java index fdc03adf64..05faf32105 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1526,7 +1526,7 @@ public void testImplementingInterface_GroovyGenericsIncorrectlyExtendingJavaGene "----------\n" + "1. ERROR in p\\C.groovy (at line 2)\n" + "\tpublic class C implements Iii {\n" + - "\t ^^^^^^^^^^^^\n" + + "\t ^^^\n" + "Groovy:The type String is not a valid substitute for the bounded parameter \n" + "----------\n"); } @@ -1638,10 +1638,9 @@ public void testHalfFinishedGenericsProgram() { public void testHalfFinishedGenericsProgramWithCorrectSuppression() { String[] sources = { "Demo.groovy", - "public class Demo {\n"+ - "\n"+ - "@SuppressWarnings(\"rawtypes\")\n"+ // should cause no warnings - "List myList;\n"+ + "class Demo {\n" + + " @SuppressWarnings('rawtypes')\n" + // should suppress the warning + " List myList\n" + "}", }; @@ -1652,10 +1651,9 @@ public void testHalfFinishedGenericsProgramWithCorrectSuppression() { public void testHalfFinishedGenericsProgramWithCorrectSuppressionAtTheTypeLevel() { String[] sources = { "Demo.groovy", - "@SuppressWarnings(\"rawtypes\")\n"+ // should cause no warnings - "public class Demo {\n"+ - "\n"+ - "List myList;\n"+ + "@SuppressWarnings('rawtypes')\n" + // should suppress the warning + "class Demo {\n"+ + " List myList\n"+ "}", }; @@ -1666,17 +1664,16 @@ public void testHalfFinishedGenericsProgramWithCorrectSuppressionAtTheTypeLevel( public void testHalfFinishedGenericsProgramWithUnnecessarySuppression() { String[] sources = { "Demo.groovy", - "public class Demo {\n"+ - "\n"+ - "@SuppressWarnings(\"unchecked\")\n"+ // unnecessary suppression - "List myList;\n"+ + "class Demo {\n" + + " @SuppressWarnings('unchecked')\n" + // unnecessary suppression + " List myList\n" + "}", }; runNegativeTest(sources, "----------\n" + - "1. WARNING in Demo.groovy (at line 3)\n" + - "\t@SuppressWarnings(\"unchecked\")\n" + + "1. WARNING in Demo.groovy (at line 2)\n" + + "\t@SuppressWarnings('unchecked')\n" + "\t ^^^^^^^^^^^\n" + "Unnecessary @SuppressWarnings(\"unchecked\")\n" + "----------\n"); diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java index 349672fba2..6ed936e4dd 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java @@ -977,7 +977,7 @@ public void testRecursion_GR531_4() { "----------\n" + "2. ERROR in XXX.groovy (at line 1)\n" + "\tinterface XXX extends XXX {\n" + - "\t ^^^^\n" + + "\t ^^^\n" + "Cycle detected: the type XXX cannot extend/implement itself or one of its own member types\n" + "----------\n"); } diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/TraitsTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/TraitsTests.java index a655694f8e..a52c651a8e 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/TraitsTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/TraitsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -894,7 +894,7 @@ public void testTraits40() { "----------\n" + "2. ERROR in Sample.groovy (at line 6)\n" + "\tclass MyClass implements MyTrait {\n" + - "\t ^^^^^^^^\n" + + "\t ^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @@ -928,7 +928,7 @@ public void testTraits41() { "----------\n" + "2. ERROR in Sample.groovy (at line 7)\n" + "\tclass MyClass implements MyTrait {\n" + - "\t ^^^^^^^^\n" + + "\t ^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @@ -961,7 +961,7 @@ public void testTraits42() { "----------\n" + "2. ERROR in Sample.groovy (at line 6)\n" + "\tclass MyClass implements MyTrait {\n" + - "\t ^^^^^^^^\n" + + "\t ^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } @@ -996,7 +996,7 @@ public void testTraits43() { "----------\n" + "2. ERROR in Sample.groovy (at line 8)\n" + "\tclass MyClass implements MyTrait {\n" + - "\t ^^^^^^^^\n" + + "\t ^^^^^^^\n" + "The type MyTrait cannot be a superinterface of MyClass; a superinterface must be an interface\n" + "----------\n"); } diff --git a/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java index 20fc6201bb..507198fa4a 100644 --- a/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java +++ b/base/org.codehaus.groovy24/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -261,8 +261,7 @@ public Object run() { return null; //new Reduction(Tpken.EOF); } - // GRECLIPSE: from private->protected - protected void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { + private void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { // straight xstream output of AST String formatProp = System.getProperty("ANTLR.AST".toLowerCase()); // uppercase to hide from jarjar @@ -340,18 +339,17 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc convertGroovy(ast); - // GRECLIPSE edit - //if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { - // does it look broken (i.e. have we built a script for it containing rubbish) - boolean hasNoMethods = output.getMethods().isEmpty(); - if (hasNoMethods && sourceUnit.getErrorCollector().hasErrors() && looksBroken(output)) { + // GRECLIPSE add -- does it look broken (i.e. have we built a script for it containing rubbish) + if (looksBroken(output) && output.getMethods().isEmpty() && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); } - if (output.getStatementBlock().isEmpty() && hasNoMethods && output.getClasses().isEmpty()) { + // GRECLIPSE end + if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { + // GRECLIPSE add if (ast == null && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); } - // GRECLIPSE end + // GRECLIPSE end output.addStatement(ReturnStatement.RETURN_NULL_OR_VOID); } @@ -384,27 +382,23 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc } // GRECLIPSE add - private boolean looksBroken(ModuleNode moduleNode) { + private static boolean looksBroken(ModuleNode moduleNode) { List classes = moduleNode.getClasses(); if (classes.size() != 1 || !classes.get(0).isScript()) { return false; } - BlockStatement statementBlock = moduleNode.getStatementBlock(); - if (statementBlock.isEmpty()) { + if (moduleNode.getStatementBlock().isEmpty()) { return true; } // Is it just a constant expression containing the word error? - // do we need to change it from ERROR to something more unlikely? - List statements = statementBlock.getStatements(); - if (statements != null && statements.size() == 1) { + // TODO: Do we need to change it from ERROR to something more unlikely? + List statements = moduleNode.getStatementBlock().getStatements(); + if (statements.size() == 1) { Statement statement = statements.get(0); if (statement instanceof ExpressionStatement) { Expression expression = ((ExpressionStatement) statement).getExpression(); - if (expression instanceof ConstantExpression) { - // ERROR node set at unknownAST - if (expression.toString().equals("ConstantExpression[ERROR]")) { - return true; - } + if (expression instanceof ConstantExpression && expression.getText().equals("ERROR")) { + return true; } } } @@ -1367,12 +1361,7 @@ protected void fieldDef(AST fieldDef) { protected ClassNode[] interfaces(AST node) { List interfaceList = new ArrayList(); for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) { - // GRECLIPSE edit - //interfaceList.add(makeTypeWithArguments(implementNode)); - ClassNode cn = makeTypeWithArguments(implementNode); - configureAST(cn, implementNode); - interfaceList.add(cn); - // GRECLIPSE end + interfaceList.add(makeTypeWithArguments(implementNode)); } ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; if (!interfaceList.isEmpty()) { @@ -3464,11 +3453,6 @@ private ClassNode addTypeArguments(ClassNode basicType, AST node) { List typeArgumentList = getTypeArgumentsList(node); // a 0-length type argument list means we face the diamond operator basicType.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); - // GRECLIPSE add - // super type source locations is not right, so set them here - // GRECLIPSE: What to do about generics? - //configureAST(basicType, rootNode); - // GRECLIPSE end return basicType; } @@ -3682,8 +3666,7 @@ protected void configureAST(ASTNode node, AST ast) { // openning '{', but shouldn't. If the new sloc is larger than the // one being set, then ignore it and don't reset. Also numbers can // result in an expression node that includes trailing whitespaces. - if ((node instanceof VariableExpression || - (node instanceof ConstantExpression && ast.getType() == EXPR)) && + if ((node instanceof VariableExpression || (node instanceof ConstantExpression && ast.getType() == EXPR)) && node.getEnd() > 0 && startoffset <= node.getStart() && endoffset >= node.getEnd()) { return; } diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java index e6d9e09d89..8432e40e11 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -262,8 +262,7 @@ public Object run() { return null; //new Reduction(Tpken.EOF); } - // GRECLIPSE: from private->protected - protected void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { + private void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { // straight xstream output of AST String formatProp = System.getProperty("ANTLR.AST".toLowerCase()); // uppercase to hide from jarjar @@ -341,18 +340,17 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc convertGroovy(ast); - // GRECLIPSE edit - //if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { - // does it look broken (i.e. have we built a script for it containing rubbish) - boolean hasNoMethods = output.getMethods().isEmpty(); - if (hasNoMethods && sourceUnit.getErrorCollector().hasErrors() && looksBroken(output)) { + // GRECLIPSE add -- does it look broken (i.e. have we built a script for it containing rubbish) + if (looksBroken(output) && output.getMethods().isEmpty() && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); } - if (output.getStatementBlock().isEmpty() && hasNoMethods && output.getClasses().isEmpty()) { + // GRECLIPSE end + if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { + // GRECLIPSE add if (ast == null && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); } - // GRECLIPSE end + // GRECLIPSE end output.addStatement(ReturnStatement.RETURN_NULL_OR_VOID); } @@ -385,27 +383,23 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc } // GRECLIPSE add - private boolean looksBroken(ModuleNode moduleNode) { + private static boolean looksBroken(ModuleNode moduleNode) { List classes = moduleNode.getClasses(); if (classes.size() != 1 || !classes.get(0).isScript()) { return false; } - BlockStatement statementBlock = moduleNode.getStatementBlock(); - if (statementBlock.isEmpty()) { + if (moduleNode.getStatementBlock().isEmpty()) { return true; } // Is it just a constant expression containing the word error? - // do we need to change it from ERROR to something more unlikely? - List statements = statementBlock.getStatements(); - if (statements != null && statements.size() == 1) { + // TODO: Do we need to change it from ERROR to something more unlikely? + List statements = moduleNode.getStatementBlock().getStatements(); + if (statements.size() == 1) { Statement statement = statements.get(0); if (statement instanceof ExpressionStatement) { Expression expression = ((ExpressionStatement) statement).getExpression(); - if (expression instanceof ConstantExpression) { - // ERROR node set at unknownAST - if (expression.toString().equals("ConstantExpression[ERROR]")) { - return true; - } + if (expression instanceof ConstantExpression && expression.getText().equals("ERROR")) { + return true; } } } @@ -1026,10 +1020,7 @@ protected void enumConstantDef(AST node) { } // GRECLIPSE edit //FieldNode enumField = EnumHelper.addEnumConstant(classNode, identifier, init); - ClassNode nonDeclaredTypeOfEnumValue = - ClassHelper.make(classNode.getName()); - nonDeclaredTypeOfEnumValue.setRedirect(classNode); - FieldNode enumField = EnumHelper.addEnumConstant(nonDeclaredTypeOfEnumValue, classNode, identifier, init, savedLine, savedColumn); + FieldNode enumField = EnumHelper.addEnumConstant(classNode, classNode, identifier, init, savedLine, savedColumn); enumField.setNameStart(locations.findOffset(savedLine, savedColumn)); enumField.setNameEnd(enumField.getNameStart() + identifier.length() - 1); // GRECLIPSE end @@ -1380,12 +1371,7 @@ public static Expression getDefaultValueForPrimitive(ClassNode type) { protected ClassNode[] interfaces(AST node) { List interfaceList = new ArrayList(); for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) { - // GRECLIPSE edit - //interfaceList.add(makeTypeWithArguments(implementNode)); - ClassNode cn = makeTypeWithArguments(implementNode); - configureAST(cn, implementNode); - interfaceList.add(cn); - // GRECLIPSE end + interfaceList.add(makeTypeWithArguments(implementNode)); } ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; if (!interfaceList.isEmpty()) { @@ -1609,13 +1595,11 @@ protected AnnotationNode annotation(AST annotationNode) { return annotatedNode; } - // Statements //------------------------------------------------------------------------- protected Statement statement(AST node) { - // GRECLIPSE add - // GRECLIPSE-1038 avoid NPE on bad code + // GRECLIPSE add -- avoid NPEs on bad code if (node == null) return new EmptyStatement(); // GRECLIPSE end Statement statement = null; @@ -2121,7 +2105,6 @@ protected Statement whileStatement(AST whileNode) { return whileStatement; } - // Expressions //------------------------------------------------------------------------- @@ -2612,16 +2595,6 @@ protected Expression methodPointerExpression(AST node) { return methodPointerExpression; } -/* commented out due to groovy.g non-determinisms - protected Expression defaultMethodPointerExpression(AST node) { - AST exprNode = node.getFirstChild(); - String methodName = exprNode.toString(); - MethodPointerExpression methodPointerExpression = new MethodPointerExpression(null, methodName); - configureAST(methodPointerExpression, node); - return methodPointerExpression; - } -*/ - protected Expression listExpression(AST listNode) { List expressions = new ArrayList(); AST elist = listNode.getFirstChild(); @@ -2709,7 +2682,6 @@ protected MapEntryExpression mapEntryExpression(AST node) { } } - protected Expression instanceofExpression(AST node) { AST leftNode = node.getFirstChild(); Expression leftExpression = expression(leftNode); @@ -2791,7 +2763,6 @@ protected Expression castExpression(AST castNode) { return castExpression; } - protected Expression indexExpression(AST indexNode) { AST bracket = indexNode.getFirstChild(); AST leftNode = bracket.getNextSibling(); @@ -3488,11 +3459,6 @@ private ClassNode addTypeArguments(ClassNode basicType, AST node) { List typeArgumentList = getTypeArgumentsList(node); // a 0-length type argument list means we face the diamond operator basicType.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); - // GRECLIPSE add - // super type source locations is not right, so set them here - // GRECLIPSE: What to do about generics? - //configureAST(basicType, rootNode); - // GRECLIPSE end return basicType; } @@ -3554,6 +3520,20 @@ protected ClassNode makeType(AST typeNode) { // GRECLIPSE edit //answer = makeType(node).makeArray(); answer = makeTypeWithArguments(node).makeArray(); + + // check array node for trailing whitespace + GroovySourceAST root = (GroovySourceAST) node; + int start = locations.findOffset(root.getLine(), root.getColumn()); + int until = locations.findOffset(root.getLineLast(), root.getColumnLast()); + char[] sourceChars = getController().readSourceRange(start, until - start); + if (sourceChars != null) { + int i = (sourceChars.length - 1); + while (i >= 0 && Character.isWhitespace(sourceChars[i])) { + i -= 1; until -= 1; + } + int[] row_col = locations.getRowCol(until); + root.setLineLast(row_col[0]); root.setColumnLast(row_col[1]); + } // GRECLIPSE end } else { checkTypeArgs(node, false); @@ -3584,17 +3564,6 @@ private boolean checkTypeArgs(AST node, boolean seenTypeArgs) { return seenTypeArgs; } - /** - * Performs a name resolution to see if the given name is a type from imports, - * aliases or newly created classes - */ - /*protected String resolveTypeName(String name, boolean safe) { - if (name == null) { - return null; - } - return resolveNewClassOrName(name, safe); - }*/ - /** * Extracts an identifier from the Antlr AST and then performs a name resolution * to see if the given name is a type from imports, aliases or newly created classes @@ -3662,11 +3631,9 @@ protected String label(AST labelNode) { return identifier(node); } - // Helper methods //------------------------------------------------------------------------- - /** * Returns true if the modifiers flags contain a visibility modifier */ @@ -3722,8 +3689,7 @@ protected void configureAST(ASTNode node, AST ast) { // openning '{', but shouldn't. If the new sloc is larger than the // one being set, then ignore it and don't reset. Also numbers can // result in an expression node that includes trailing whitespaces. - if ((node instanceof VariableExpression || - (node instanceof ConstantExpression && ast.getType() == EXPR)) && + if ((node instanceof VariableExpression || (node instanceof ConstantExpression && ast.getType() == EXPR)) && node.getEnd() > 0 && startoffset <= node.getStart() && endoffset >= node.getEnd()) { return; } @@ -3751,7 +3717,6 @@ protected String getFirstChildText(AST node) { return child != null ? child.getText() : null; } - public static boolean isType(int typeCode, AST node) { return node != null && node.getType() == typeCode; } diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java index 604508f02a..82a99d73fc 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java @@ -198,14 +198,10 @@ public void addError(String msg, ASTNode expr) { int start = expr.getStart(); int end = expr.getEnd() - 1; if (expr instanceof ClassNode) { - // assume we have a class declaration ClassNode cn = (ClassNode) expr; if (cn.getNameEnd() > 0) { start = cn.getNameStart(); end = cn.getNameEnd(); - } else if (cn.getComponentType() != null) { - // avoid extra whitespace after closing ] - end -= 1; } } else if (expr instanceof DeclarationExpression) { // assume that we just want to underline the variable declaration @@ -213,7 +209,7 @@ public void addError(String msg, ASTNode expr) { Expression lhs = decl.getLeftExpression(); start = lhs.getStart(); // avoid extra space before = if a variable - end = lhs instanceof VariableExpression ? start + lhs.getText().length() -1: lhs.getEnd() -1; + end = lhs instanceof VariableExpression ? start + lhs.getText().length() - 1: lhs.getEnd() - 1; } // GRECLIPSE end SourceUnit source = getSourceUnit(); diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/SourceUnit.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/SourceUnit.java index 552a884002..33817c2b37 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/SourceUnit.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/control/SourceUnit.java @@ -25,6 +25,7 @@ import groovyjarjarantlr.NoViableAltForCharException; import groovy.lang.GroovyClassLoader; import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.activator.GroovyActivator; import org.codehaus.groovy.ast.Comment; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.control.io.FileReaderSource; @@ -37,6 +38,8 @@ import org.codehaus.groovy.syntax.Reduction; import org.codehaus.groovy.syntax.SyntaxException; import org.codehaus.groovy.tools.Utilities; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import java.io.File; import java.io.IOException; @@ -139,7 +142,6 @@ public String getName() { return name; } - /** * Returns the Concrete Syntax Tree produced during parse()ing. */ @@ -155,7 +157,6 @@ public ModuleNode getAST() { return this.ast; } - /** * Convenience routine, primarily for use by the InteractiveShell, * that returns true if parse() failed with an unexpected EOF. @@ -191,11 +192,9 @@ protected boolean isEofToken(groovyjarjarantlr.Token token) { return token.getType() == groovyjarjarantlr.Token.EOF_TYPE; } - //--------------------------------------------------------------------------- // FACTORIES - /** * A convenience routine to create a standalone SourceUnit on a String * with defaults for almost everything that is configurable. @@ -363,4 +362,21 @@ public void addErrorAndContinue(SyntaxException se) throws CompilationFailedExce } public ReaderSource getSource() { return source; } + + // GRECLIPSE add + public char[] readSourceRange(int offset, int length) { + try (Reader reader = getSource().getReader()) { + reader.skip(offset); int n = length; + final char[] code = new char[n]; + while (n > 0) { + n -= reader.read(code, length - n, n); + } + return code; + } catch (Exception e) { + GroovyActivator.getDefault().getLog().log( + new Status(IStatus.ERROR, GroovyActivator.PLUGIN_ID, "Error reading Groovy source", e)); + } + return null; + } + // GRECLIPSE end } diff --git a/base/org.codehaus.groovy26/src/org/apache/groovy/parser/antlr4/AstBuilder.java b/base/org.codehaus.groovy26/src/org/apache/groovy/parser/antlr4/AstBuilder.java index 2c32a35381..1e97d3faa2 100644 --- a/base/org.codehaus.groovy26/src/org/apache/groovy/parser/antlr4/AstBuilder.java +++ b/base/org.codehaus.groovy26/src/org/apache/groovy/parser/antlr4/AstBuilder.java @@ -538,11 +538,10 @@ public ModuleNode visitCompilationUnit(CompilationUnitContext ctx) { moduleNode.setLastLineNumber(locationSupport.getEndLine()); moduleNode.setLastColumnNumber(locationSupport.getEndColumn()); BlockStatement blockStatement = moduleNode.getStatementBlock(); - boolean hasScriptStatements = blockStatement != null && asBoolean(blockStatement.getStatements()); - if (hasScriptStatements || asBoolean(moduleNode.getMethods())) { + if (!blockStatement.isEmpty() || !moduleNode.getMethods().isEmpty()) { ASTNode alpha = findAlpha(blockStatement, moduleNode.getMethods()); ASTNode omega = findOmega(blockStatement, moduleNode.getMethods()); - if (hasScriptStatements) { + if (!blockStatement.isEmpty()) { blockStatement.setStart(alpha.getStart()); blockStatement.setLineNumber(alpha.getLineNumber()); blockStatement.setColumnNumber(alpha.getColumnNumber()); @@ -550,7 +549,7 @@ public ModuleNode visitCompilationUnit(CompilationUnitContext ctx) { blockStatement.setLastLineNumber(omega.getLastLineNumber()); blockStatement.setLastColumnNumber(omega.getLastColumnNumber()); } - if (asBoolean(moduleNode.getClasses())) { + if (!moduleNode.getClasses().isEmpty()) { ClassNode scriptClass = moduleNode.getClasses().get(0); scriptClass.setStart(alpha.getStart()); scriptClass.setLineNumber(alpha.getLineNumber()); @@ -577,8 +576,8 @@ public ModuleNode visitCompilationUnit(CompilationUnitContext ctx) { // GRECLIPSE add /** Returns the first method or statement node in the script. */ private ASTNode findAlpha(BlockStatement blockStatement, List methods) { - MethodNode method = (asBoolean(methods) ? methods.get(0) : null); - Statement statement = (blockStatement != null && asBoolean(blockStatement.getStatements()) ? blockStatement.getStatements().get(0) : null); + MethodNode method = (!methods.isEmpty() ? methods.get(0) : null); + Statement statement = (!blockStatement.isEmpty() ? blockStatement.getStatements().get(0) : null); if (method == null && (statement == null || (statement.getStart() == 0 && statement.getLength() == 0))) { // a script with no methods or statements; use a synthetic statement after the end of the package declaration/import statements statement = createEmptyScriptStatement(); @@ -590,8 +589,8 @@ private ASTNode findAlpha(BlockStatement blockStatement, List method /** Returns the final method or statement node in the script. */ private ASTNode findOmega(BlockStatement blockStatement, List methods) { - MethodNode method = (asBoolean(methods) ? last(methods) : null); - Statement statement = (blockStatement != null && asBoolean(blockStatement.getStatements()) ? last(blockStatement.getStatements()) : null); + MethodNode method = (!methods.isEmpty() ? last(methods) : null); + Statement statement = (!blockStatement.isEmpty() ? last(blockStatement.getStatements()) : null); if (method == null && (statement == null || (statement.getStart() == 0 && statement.getLength() == 0))) { // a script with no methods or statements; add a synthetic statement after the end of the package declaration/import statements statement = createEmptyScriptStatement(); diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java index dd514d8acb..c0740fcb87 100644 --- a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -131,6 +131,9 @@ import java.util.List; import java.util.Set; +import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean; +import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last; + /** * A parser plugin which adapts the JSR Antlr Parser to the Groovy runtime * @@ -262,8 +265,7 @@ public Object run() { return null; //new Reduction(Tpken.EOF); } - // GRECLIPSE: from private->protected - protected void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { + private void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { // straight xstream output of AST String formatProp = System.getProperty("ANTLR.AST".toLowerCase()); // uppercase to hide from jarjar @@ -341,18 +343,17 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc convertGroovy(ast); - // GRECLIPSE edit - //if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { - // does it look broken (i.e. have we built a script for it containing rubbish) - boolean hasNoMethods = output.getMethods().isEmpty(); - if (hasNoMethods && sourceUnit.getErrorCollector().hasErrors() && looksBroken(output)) { + // GRECLIPSE add -- does it look broken (i.e. have we built a script for it containing rubbish) + if (looksBroken(output) && output.getMethods().isEmpty() && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); } - if (output.getStatementBlock().isEmpty() && hasNoMethods && output.getClasses().isEmpty()) { + // GRECLIPSE end + if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { + // GRECLIPSE add if (ast == null && sourceUnit.getErrorCollector().hasErrors()) { output.setEncounteredUnrecoverableError(true); } - // GRECLIPSE end + // GRECLIPSE end output.addStatement(ReturnStatement.RETURN_NULL_OR_VOID); } @@ -372,7 +373,43 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc } // GRECLIPSE add - fixModuleNodeLocations(); + output.setStart(0); + output.setLineNumber(1); + output.setColumnNumber(1); + output.setEnd(locations.getEnd()); + output.setLastLineNumber(locations.getEndLine()); + output.setLastColumnNumber(locations.getEndColumn()); + BlockStatement blockStatement = output.getStatementBlock(); + if (!blockStatement.isEmpty() || !output.getMethods().isEmpty()) { + ASTNode first = getFirst(blockStatement, output.getMethods()); + ASTNode last = getLast(blockStatement, output.getMethods()); + if (!blockStatement.isEmpty()) { + blockStatement.setStart(first.getStart()); + blockStatement.setLineNumber(first.getLineNumber()); + blockStatement.setColumnNumber(first.getColumnNumber()); + blockStatement.setEnd(last.getEnd()); + blockStatement.setLastLineNumber(last.getLastLineNumber()); + blockStatement.setLastColumnNumber(last.getLastColumnNumber()); + } + if (!output.getClasses().isEmpty()) { + ClassNode scriptClass = output.getClasses().get(0); + scriptClass.setStart(first.getStart()); + scriptClass.setLineNumber(first.getLineNumber()); + scriptClass.setColumnNumber(first.getColumnNumber()); + scriptClass.setEnd(last.getEnd()); + scriptClass.setLastLineNumber(last.getLastLineNumber()); + scriptClass.setLastColumnNumber(last.getLastColumnNumber()); + + // fix the run method to contain the start and end locations of the statement block + MethodNode runMethod = scriptClass.getDeclaredMethod("run", Parameter.EMPTY_ARRAY); + runMethod.setStart(first.getStart()); + runMethod.setLineNumber(first.getLineNumber()); + runMethod.setColumnNumber(first.getColumnNumber()); + runMethod.setEnd(last.getEnd()); + runMethod.setLastLineNumber(last.getLastLineNumber()); + runMethod.setLastColumnNumber(last.getLastColumnNumber()); + } + } // GRECLIPSE end } catch (ASTRuntimeException e) { @@ -385,32 +422,79 @@ public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduc } // GRECLIPSE add - private boolean looksBroken(ModuleNode moduleNode) { + private static boolean looksBroken(ModuleNode moduleNode) { List classes = moduleNode.getClasses(); if (classes.size() != 1 || !classes.get(0).isScript()) { return false; } - BlockStatement statementBlock = moduleNode.getStatementBlock(); - if (statementBlock.isEmpty()) { + if (moduleNode.getStatementBlock().isEmpty()) { return true; } // Is it just a constant expression containing the word error? - // do we need to change it from ERROR to something more unlikely? - List statements = statementBlock.getStatements(); - if (statements != null && statements.size() == 1) { + // TODO: Do we need to change it from ERROR to something more unlikely? + List statements = moduleNode.getStatementBlock().getStatements(); + if (statements.size() == 1) { Statement statement = statements.get(0); if (statement instanceof ExpressionStatement) { Expression expression = ((ExpressionStatement) statement).getExpression(); - if (expression instanceof ConstantExpression) { - // ERROR node set at unknownAST - if (expression.toString().equals("ConstantExpression[ERROR]")) { - return true; - } + if (expression instanceof ConstantExpression && expression.getText().equals("ERROR")) { + return true; } } } return false; } + + /** Returns the first ast node in the script, either a method or a statement. */ + private ASTNode getFirst(BlockStatement blockStatement, List methods) { + MethodNode method = (!methods.isEmpty() ? methods.get(0) : null); + Statement statement = (!blockStatement.isEmpty() ? blockStatement.getStatements().get(0) : null); + if (method == null && (statement == null || (statement.getStart() == 0 && statement.getLength() == 0))) { + // a script with no methods or statements; add a synthetic statement after the end of the package declaration/import statements + statement = createEmptyScriptStatement(); + } + int statementStart = (statement != null ? statement.getStart() : Integer.MAX_VALUE); + int methodStart = (method != null ? method.getStart() : Integer.MAX_VALUE); + return (statementStart <= methodStart ? statement : method); + } + + /** Returns the last ast node in the script, either a method or a statement. */ + private ASTNode getLast(BlockStatement blockStatement, List methods) { + MethodNode method = (!methods.isEmpty() ? last(methods) : null); + Statement statement = (!blockStatement.isEmpty() ? last(blockStatement.getStatements()) : null); + if (method == null && (statement == null || (statement.getStart() == 0 && statement.getLength() == 0))) { + // a script with no methods or statements; add a synthetic statement after the end of the package declaration/import statements + statement = createEmptyScriptStatement(); + } + int statementStart = (statement != null ? statement.getEnd() : Integer.MIN_VALUE); + int methodStart = (method != null ? method.getStart() : Integer.MIN_VALUE); + return (statementStart >= methodStart ? statement : method); + } + + /** Creates a synthetic statement that starts after the last import or package statement. */ + private Statement createEmptyScriptStatement() { + Statement statement = ReturnStatement.RETURN_NULL_OR_VOID; + ASTNode target = null; + if (asBoolean(output.getImports())) { + target = last(output.getImports()); + } else if (output.hasPackage()) { + target = output.getPackage(); + } + if (target != null) { + // import/package nodes do not include trailing semicolon, so use end of line instead of end of node + int off = Math.min(locations.findOffset(target.getLastLineNumber() + 1, 1), locations.getEnd() - 1); + int[] row_col = locations.getRowCol(off); + + statement = new ReturnStatement(ConstantExpression.NULL); + statement.setStart(off); + statement.setEnd(off); + statement.setLineNumber(row_col[0]); + statement.setColumnNumber(row_col[1]); + statement.setLastLineNumber(row_col[0]); + statement.setLastColumnNumber(row_col[1]); + } + return statement; + } // GRECLIPSE end /** @@ -1378,12 +1462,7 @@ public static Expression getDefaultValueForPrimitive(ClassNode type) { protected ClassNode[] interfaces(AST node) { List interfaceList = new ArrayList(); for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) { - // GRECLIPSE edit - //interfaceList.add(makeTypeWithArguments(implementNode)); - ClassNode cn = makeTypeWithArguments(implementNode); - configureAST(cn, implementNode); - interfaceList.add(cn); - // GRECLIPSE end + interfaceList.add(makeTypeWithArguments(implementNode)); } ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; if (!interfaceList.isEmpty()) { @@ -1607,6 +1686,27 @@ protected AnnotationNode annotation(AST annotationNode) { return annotatedNode; } + // GRECLIPSE add + protected void configureAnnotationAST(AnnotationNode node, AST ast) { + if (ast == null) { + throw new ASTRuntimeException(ast, "PARSER BUG: Tried to configure " + node.getClass().getName() + " with null AST"); + } + if (!(ast instanceof GroovySourceAST)) { + throw new ASTRuntimeException(ast, "PARSER BUG: Expected a GroovySourceAST node for annotation, but got: " + ast.getClass().getName()); + } + // Structure of incoming parameter 'ast': + // ANNOTATION + // - down='annotationName' (IDENT:84) + configureAST(node, ast.getFirstChild()); + configureAST(node.getClassNode(), ast.getFirstChild()); + // save the full source range of the annotation for future use + long start = locations.findOffset(ast.getLine(), ast.getColumn()); + long until = locations.findOffset(((GroovySourceAST) ast).getLineLast(), ((GroovySourceAST) ast).getColumnLast()); + node.setNodeMetaData("source.offsets", (start << 32) | until); // pack the two offsets into one long integer value + + node.setEnd(node.getEnd() - 1); // Eclipse wants this for error reporting + } + // GRECLIPSE end // Statements //------------------------------------------------------------------------- @@ -2119,7 +2219,6 @@ protected Statement whileStatement(AST whileNode) { return whileStatement; } - // Expressions //------------------------------------------------------------------------- @@ -2610,16 +2709,6 @@ protected Expression methodPointerExpression(AST node) { return methodPointerExpression; } -/* commented out due to groovy.g non-determinisms - protected Expression defaultMethodPointerExpression(AST node) { - AST exprNode = node.getFirstChild(); - String methodName = exprNode.toString(); - MethodPointerExpression methodPointerExpression = new MethodPointerExpression(null, methodName); - configureAST(methodPointerExpression, node); - return methodPointerExpression; - } -*/ - protected Expression listExpression(AST listNode) { List expressions = new ArrayList(); AST elist = listNode.getFirstChild(); @@ -2707,7 +2796,6 @@ protected MapEntryExpression mapEntryExpression(AST node) { } } - protected Expression instanceofExpression(AST node) { AST leftNode = node.getFirstChild(); Expression leftExpression = expression(leftNode); @@ -2789,7 +2877,6 @@ protected Expression castExpression(AST castNode) { return castExpression; } - protected Expression indexExpression(AST indexNode) { AST bracket = indexNode.getFirstChild(); AST leftNode = bracket.getNextSibling(); @@ -3486,11 +3573,6 @@ private ClassNode addTypeArguments(ClassNode basicType, AST node) { List typeArgumentList = getTypeArgumentsList(node); // a 0-length type argument list means we face the diamond operator basicType.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); - // GRECLIPSE add - // super type source locations is not right, so set them here - // GRECLIPSE: What to do about generics? - //configureAST(basicType, rootNode); - // GRECLIPSE end return basicType; } @@ -3552,6 +3634,20 @@ protected ClassNode makeType(AST typeNode) { // GRECLIPSE edit //answer = makeType(node).makeArray(); answer = makeTypeWithArguments(node).makeArray(); + + // check array node for trailing whitespace + GroovySourceAST root = (GroovySourceAST) node; + int start = locations.findOffset(root.getLine(), root.getColumn()); + int until = locations.findOffset(root.getLineLast(), root.getColumnLast()); + char[] sourceChars = getController().readSourceRange(start, until - start); + if (sourceChars != null) { + int i = (sourceChars.length - 1); + while (i >= 0 && Character.isWhitespace(sourceChars[i])) { + i -= 1; until -= 1; + } + int[] row_col = locations.getRowCol(until); + root.setLineLast(row_col[0]); root.setColumnLast(row_col[1]); + } // GRECLIPSE end } else { checkTypeArgs(node, false); @@ -3582,17 +3678,6 @@ private boolean checkTypeArgs(AST node, boolean seenTypeArgs) { return seenTypeArgs; } - /** - * Performs a name resolution to see if the given name is a type from imports, - * aliases or newly created classes - */ - /*protected String resolveTypeName(String name, boolean safe) { - if (name == null) { - return null; - } - return resolveNewClassOrName(name, safe); - }*/ - /** * Extracts an identifier from the Antlr AST and then performs a name resolution * to see if the given name is a type from imports, aliases or newly created classes @@ -3660,11 +3745,9 @@ protected String label(AST labelNode) { return identifier(node); } - // Helper methods //------------------------------------------------------------------------- - /** * Returns true if the modifiers flags contain a visibility modifier */ @@ -3748,7 +3831,6 @@ protected String getFirstChildText(AST node) { return child != null ? child.getText() : null; } - public static boolean isType(int typeCode, AST node) { return node != null && node.getType() == typeCode; } @@ -3801,133 +3883,4 @@ protected void dumpTree(AST ast) { protected void dump(AST node) { System.out.println("Type: " + getTokenName(node) + " text: " + node.getText()); } - - // GRECLIPSE add - private void fixModuleNodeLocations() { - // only occurs if in a script - - output.setStart(0); - output.setEnd(locations.getEnd()); - output.setLineNumber(1); - output.setColumnNumber(1); - output.setLastColumnNumber(locations.getEndColumn()); - output.setLastLineNumber(locations.getEndLine()); - - // start location should be the start of either the first method or statement - // end location should be the end of either the last method or statement - BlockStatement statement = output.getStatementBlock(); - List methods = output.getMethods(); - if (hasScriptStatements(statement) || hasScriptMethods(methods)) { - ASTNode first = getFirst(statement, methods); - ASTNode last = getLast(statement, methods); - if (hasScriptStatements(statement)) { - statement.setStart(first.getStart()); - statement.setLineNumber(first.getLineNumber()); - statement.setColumnNumber(first.getColumnNumber()); - statement.setEnd(last.getEnd()); - statement.setLastLineNumber(last.getLastLineNumber()); - statement.setLastColumnNumber(last.getLastColumnNumber()); - } - if (!output.getClasses().isEmpty()) { - ClassNode scriptClass = output.getClasses().get(0); - scriptClass.setStart(first.getStart()); - scriptClass.setLineNumber(first.getLineNumber()); - scriptClass.setColumnNumber(first.getColumnNumber()); - scriptClass.setEnd(last.getEnd()); - scriptClass.setLastLineNumber(last.getLastLineNumber()); - scriptClass.setLastColumnNumber(last.getLastColumnNumber()); - - // fix the run method to contain the start and end locations of the statement block - MethodNode runMethod = scriptClass.getDeclaredMethod("run", Parameter.EMPTY_ARRAY); - runMethod.setStart(first.getStart()); - runMethod.setLineNumber(first.getLineNumber()); - runMethod.setColumnNumber(first.getColumnNumber()); - runMethod.setEnd(last.getEnd()); - runMethod.setLastLineNumber(last.getLastLineNumber()); - runMethod.setLastColumnNumber(last.getLastColumnNumber()); - } - } - } - - private boolean hasScriptMethods(List methods) { - return methods != null && !methods.isEmpty(); - } - - private boolean hasScriptStatements(BlockStatement statement) { - return statement != null && statement.getStatements() != null && !statement.getStatements().isEmpty(); - } - - /** Returns the first ast node in the script, either a method or a statement. */ - private ASTNode getFirst(BlockStatement statement, List methods) { - Statement firstStatement = hasScriptStatements(statement) ? statement.getStatements().get(0) : null; - MethodNode firstMethod = hasScriptMethods(methods) ? methods.get(0) : null; - if (firstMethod == null && (firstStatement == null || (firstStatement.getStart() == 0 && firstStatement.getLength() == 0))) { - // An empty script with no methods or statements. - // instead make a synthetic statement from the end of the package declaration/import statements - firstStatement = createSyntheticAfterImports(); - } - int statementStart = firstStatement != null ? firstStatement.getStart() : Integer.MAX_VALUE; - int methodStart = firstMethod != null ? firstMethod.getStart() : Integer.MAX_VALUE; - return statementStart <= methodStart ? firstStatement : firstMethod; - } - - /** Returns the last ast node in the script, either a method or a statement. */ - private ASTNode getLast(BlockStatement statement, List methods) { - Statement lastStatement = hasScriptStatements(statement) ? statement.getStatements().get(statement.getStatements().size() - 1) : null; - MethodNode lastMethod = hasScriptMethods(methods) ? methods.get(methods.size() - 1) : null; - if (lastMethod == null && (lastStatement == null || (lastStatement.getStart() == 0 && lastStatement.getLength() == 0))) { - // An empty script with no methods or statements. - // instead make a synthetic statement from the end of the package declaration/import statements - lastStatement = createSyntheticAfterImports(); - } - int statementStart = lastStatement != null ? lastStatement.getEnd() : Integer.MIN_VALUE; - int methodStart = lastMethod != null ? lastMethod.getStart() : Integer.MIN_VALUE; - return statementStart >= methodStart ? lastStatement : lastMethod; - } - - /** Creates a synthetic statement that starts after the last import or package statement. */ - private Statement createSyntheticAfterImports() { - Statement synthetic = ReturnStatement.RETURN_NULL_OR_VOID; - ASTNode target = null; - if (output.getImports() != null && !output.getImports().isEmpty()) { - target = output.getImports().get(output.getImports().size() - 1); - } else if (output.hasPackage()) { - target = output.getPackage(); - } - if (target != null) { - // import/package nodes do not include trailing semicolon, so use end of line instead of end of node - int off = Math.min(locations.findOffset(target.getLastLineNumber() + 1, 1), locations.getEnd() - 1); - int[] row_col = locations.getRowCol(off); - - synthetic = new ReturnStatement(ConstantExpression.NULL); - synthetic.setStart(off); - synthetic.setEnd(off); - synthetic.setLineNumber(row_col[0]); - synthetic.setColumnNumber(row_col[1]); - synthetic.setLastLineNumber(row_col[0]); - synthetic.setLastColumnNumber(row_col[1]); - } - return synthetic; - } - - protected void configureAnnotationAST(AnnotationNode node, AST ast) { - if (ast == null) { - throw new ASTRuntimeException(ast, "PARSER BUG: Tried to configure " + node.getClass().getName() + " with null AST"); - } - if (!(ast instanceof GroovySourceAST)) { - throw new ASTRuntimeException(ast, "PARSER BUG: Expected a GroovySourceAST node for annotation, but got: " + ast.getClass().getName()); - } - // Structure of incoming parameter 'ast': - // ANNOTATION - // - down='annotationName' (IDENT:84) - configureAST(node, ast.getFirstChild()); - configureAST(node.getClassNode(), ast.getFirstChild()); - // save the full source range of the annotation for future use - long start = locations.findOffset(ast.getLine(), ast.getColumn()); - long until = locations.findOffset(((GroovySourceAST) ast).getLineLast(), ((GroovySourceAST) ast).getColumnLast()); - node.setNodeMetaData("source.offsets", (start << 32) | until); // pack the two offsets into one long integer value - - node.setEnd(node.getEnd() - 1); // Eclipse wants this for error reporting - } - // GRECLIPSE end } diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java index 604508f02a..82a99d73fc 100644 --- a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java @@ -198,14 +198,10 @@ public void addError(String msg, ASTNode expr) { int start = expr.getStart(); int end = expr.getEnd() - 1; if (expr instanceof ClassNode) { - // assume we have a class declaration ClassNode cn = (ClassNode) expr; if (cn.getNameEnd() > 0) { start = cn.getNameStart(); end = cn.getNameEnd(); - } else if (cn.getComponentType() != null) { - // avoid extra whitespace after closing ] - end -= 1; } } else if (expr instanceof DeclarationExpression) { // assume that we just want to underline the variable declaration @@ -213,7 +209,7 @@ public void addError(String msg, ASTNode expr) { Expression lhs = decl.getLeftExpression(); start = lhs.getStart(); // avoid extra space before = if a variable - end = lhs instanceof VariableExpression ? start + lhs.getText().length() -1: lhs.getEnd() -1; + end = lhs instanceof VariableExpression ? start + lhs.getText().length() - 1: lhs.getEnd() - 1; } // GRECLIPSE end SourceUnit source = getSourceUnit(); diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java index 06deee1528..dd5bd4c245 100644 --- a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java @@ -25,6 +25,7 @@ import groovyjarjarantlr.NoViableAltForCharException; import groovy.lang.GroovyClassLoader; import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.activator.GroovyActivator; import org.codehaus.groovy.ast.Comment; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.control.io.FileReaderSource; @@ -37,6 +38,8 @@ import org.codehaus.groovy.syntax.Reduction; import org.codehaus.groovy.syntax.SyntaxException; import org.codehaus.groovy.tools.Utilities; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import java.io.File; import java.io.IOException; @@ -140,7 +143,6 @@ public String getName() { return name; } - /** * Returns the Concrete Syntax Tree produced during parse()ing. */ @@ -156,7 +158,6 @@ public ModuleNode getAST() { return this.ast; } - /** * Convenience routine, primarily for use by the InteractiveShell, * that returns true if parse() failed with an unexpected EOF. @@ -192,11 +193,9 @@ protected boolean isEofToken(groovyjarjarantlr.Token token) { return token.getType() == groovyjarjarantlr.Token.EOF_TYPE; } - //--------------------------------------------------------------------------- // FACTORIES - /** * A convenience routine to create a standalone SourceUnit on a String * with defaults for almost everything that is configurable. @@ -370,4 +369,21 @@ public ReaderSource getSource() { public void setSource(ReaderSource source) { this.source = source; } + + // GRECLIPSE add + public char[] readSourceRange(int offset, int length) { + try (Reader reader = getSource().getReader()) { + reader.skip(offset); int n = length; + final char[] code = new char[n]; + while (n > 0) { + n -= reader.read(code, length - n, n); + } + return code; + } catch (Exception e) { + GroovyActivator.getDefault().getLog().log( + new Status(IStatus.ERROR, GroovyActivator.PLUGIN_ID, "Error reading Groovy source", e)); + } + return null; + } + // GRECLIPSE end } diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/GroovyUtils.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/GroovyUtils.java index dfb74dbc40..05a82c178b 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/GroovyUtils.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/GroovyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,6 +67,7 @@ public static Version getGroovyVersion() { return new Version(version); } + // TODO: Replace with SourceUnit.readSourceRange when Groovy 2.5 is the minimum supported runtime. public static char[] readSourceRange(SourceUnit unit, int offset, int length) { try (Reader reader = unit.getSource().getReader()) { reader.skip(offset);