diff --git a/plugins/org.eclipse.n4js.model/emf-gen/org/eclipse/n4js/n4JS/ArrowFunction.java b/plugins/org.eclipse.n4js.model/emf-gen/org/eclipse/n4js/n4JS/ArrowFunction.java index 7a39167fa1..3ffb474761 100644 --- a/plugins/org.eclipse.n4js.model/emf-gen/org/eclipse/n4js/n4JS/ArrowFunction.java +++ b/plugins/org.eclipse.n4js.model/emf-gen/org/eclipse/n4js/n4JS/ArrowFunction.java @@ -96,6 +96,9 @@ public interface ArrowFunction extends FunctionExpression { * * * If {@link #isSingleExprImplicitReturn()} returns true, this method will return the single expression * that makes up the body of this arrow function, otherwise the behavior is undefined (might throw exception). + *

+ * In case of broken AST, this method may return null even if {@link #isSingleExprImplicitReturn()} + * returns true. * * @model kind="operation" unique="false" * annotation="http://www.eclipse.org/emf/2002/GenModel body='<%org.eclipse.n4js.n4JS.Statement%> _head = <%org.eclipse.xtext.xbase.lib.IterableExtensions%>.<<%org.eclipse.n4js.n4JS.Statement%>>head(this.getBody().getStatements());\nreturn ((<%org.eclipse.n4js.n4JS.ExpressionStatement%>) _head).getExpression();'" diff --git a/plugins/org.eclipse.n4js.model/model/N4JS.xcore b/plugins/org.eclipse.n4js.model/model/N4JS.xcore index 282cdd868e..afeda61abf 100644 --- a/plugins/org.eclipse.n4js.model/model/N4JS.xcore +++ b/plugins/org.eclipse.n4js.model/model/N4JS.xcore @@ -619,6 +619,9 @@ class ArrowFunction extends FunctionExpression { /** * If {@link #isSingleExprImplicitReturn()} returns true, this method will return the single expression * that makes up the body of this arrow function, otherwise the behavior is undefined (might throw exception). + *

+ * In case of broken AST, this method may return null even if {@link #isSingleExprImplicitReturn()} + * returns true. */ op Expression getSingleExpression() { return (body.statements.head as ExpressionStatement).expression; diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/PolyProcessor_FunctionExpression.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/PolyProcessor_FunctionExpression.xtend index 423292f337..b249e4ad45 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/PolyProcessor_FunctionExpression.xtend +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/PolyProcessor_FunctionExpression.xtend @@ -270,10 +270,12 @@ package class PolyProcessor_FunctionExpression extends AbstractPolyProcessor { } // tweak return type if (funExpr instanceof ArrowFunction) { + log(0, "===START of special handling of single-expression arrow function"); // NOTE: the next line requires the type of 'funExpr' and types of fpars to be in cache! For example: // function foo(p: {function(int):T}) {return undefined;} // foo( (i) => [i] ); tweakReturnTypeOfSingleExpressionArrowFunction(G, cache, funExpr, resultSolved); + log(0, "===END of special handling of single-expression arrow function"); } } @@ -300,11 +302,13 @@ package class PolyProcessor_FunctionExpression extends AbstractPolyProcessor { if (!arrFun.isSingleExprImplicitReturn) { return; // not applicable } - log(0, "===START of special handling of single-expression arrow function"); // Step 1) process arrFun's body, which was postponed earlier according to ASTProcessor#isPostponedNode(EObject) // Rationale: the body of a single-expression arrow function isn't a true block, so we do not have to // postpone it AND we need its types in the next step. val block = arrFun.body; + if (block === null) { + return; // broken AST + } if(!cache.postponedSubTrees.remove(block)) { throw new IllegalStateException("body of single-expression arrow function not among postponed subtrees, in resource: " + arrFun.eResource.URI); } @@ -312,6 +316,9 @@ package class PolyProcessor_FunctionExpression extends AbstractPolyProcessor { // Step 2) adjust arrFun's return type stored in arrFunTypeRef (if required) var didTweakReturnType = false; val expr = arrFun.getSingleExpression(); + if (expr === null) { + return; // broken AST + } val exprTypeRef = cache.getType(expr).value; // must now be in cache, because we just processed arrFun's body if (TypeUtils.isVoid(exprTypeRef)) { // the actual type of 'expr' is void @@ -344,7 +351,6 @@ package class PolyProcessor_FunctionExpression extends AbstractPolyProcessor { if(!didTweakReturnType) { log(1, "tweaking of return type not required"); } - log(0, "===END of special handling of single-expression arrow function"); } /** diff --git a/tests/org.eclipse.n4js.smoke.tests/src/org/eclipse/n4js/smoke/tests/GeneratedSmokeTestCases4.xtend b/tests/org.eclipse.n4js.smoke.tests/src/org/eclipse/n4js/smoke/tests/GeneratedSmokeTestCases4.xtend index 66391ad7a7..b19ddbd626 100644 --- a/tests/org.eclipse.n4js.smoke.tests/src/org/eclipse/n4js/smoke/tests/GeneratedSmokeTestCases4.xtend +++ b/tests/org.eclipse.n4js.smoke.tests/src/org/eclipse/n4js/smoke/tests/GeneratedSmokeTestCases4.xtend @@ -3121,4 +3121,15 @@ class GeneratedSmokeTestCases4 { } '''.assertNoException } + + /** + * GH-616 + */ + @Test + def void test_GH_616() { + // removing the second dot removes the exception + ''' + let r = (v) => v..foo(); + '''.assertNoException + } }