Skip to content

Commit ae67c5a

Browse files
author
mmews-n4
authored
GH-2612: As a developer I want array literals to be typed as ArrayN<...> instead of just Array<union{...}> (#2613)
* add test * first shot * adjust tests * add helper method * support type inference on indexed access * adjust test * tweaks for ArrayN inference * adjust tests * next try, added tests * next try * adjust tests * adjust tests * reducer respects structural subtype check wrt. optional properties * improved error message in case of failed poly processor * remove obsolete casts * adjust tests * adjust tests * improve nested object literals, improve non-backtracker wrt. struct. obj * adjust tests * fix concurrent exception * fix hover exception * add some tests, some src improvements * more tests, adjust tests * fix several issues: Deadlock during startup, Text Highlight and Documentation, Issue creation * adjust built-ins Array#concat to support unification of types * add tests * adjust tests * more tests / corner cases * add test, adjust tests * support parentheses around return expressions * adjusted tests * several unrelated changes * many improvements, performance, reduce unions with match, ... * add another test for bug regarding union of methods with type vars * enhance deadlock bugfix * fix handling type vars of composed methods * adjust some test expectations * fix double reporting, adjust tests, add test case * fix/mitigate hover bug * postpone to GH-2615 * incorporate review changes * bump minor version due to backwards incompatibility
1 parent 9ba8677 commit ae67c5a

File tree

88 files changed

+2112
-601
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+2112
-601
lines changed

n4js-libs/packages/n4js-cli/src/n4js/Globals.n4js

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export public const JRE_INFO_MAP = new Map<Platform, JreInfo>([
3737
[Platform.x64_sunos, null],
3838
[Platform.x32_win32, {bin: "bin/java.exe", name: "OpenJDK-jre_x86-32_windows_hotspot_2021-05-06-23-30.zip"}],
3939
[Platform.x64_win32, {bin: "bin/java.exe", name: "OpenJDK-jre_x64_windows_hotspot_2021-05-06-23-30.zip"}]]
40-
as Iterable<Iterable2<Platform, JreInfo>>);
40+
);
4141

4242

4343
@StringBased

n4js-libs/packages/org.eclipse.n4js.mangelhaft.test/test/n4js/org/eclipse/n4js/mangelhaft/test/TestExecutorTests.n4js

+3-3
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ export public class TestExecutorTests {
264264
["fixme__multiScope___success", TestStatus.passed],
265265
["fixme__multiScope___fail", TestStatus.failed],
266266
]
267-
const expectedMap = new Map<string, string>(expected as Iterable<Iterable2<string,string>>);
267+
const expectedMap = new Map<string, string>(expected);
268268
const tests: InstrumentedTest[] = []
269269
tests.push(new InstrumentedTest().load(FixmeTests).setTestObject(new FixmeTests()));
270270
const val = await this.subject.executor.runTestsAsync(tests);
@@ -286,7 +286,7 @@ export public class TestExecutorTests {
286286
["fixme__multiScope___success", TestStatus.failed],
287287
["fixme__multiScope___fail", TestStatus.skipped_fixme],
288288
]
289-
const expectedMap = new Map<string, string>(expected as Iterable<Iterable2<string,string>>);
289+
const expectedMap = new Map<string, string>(expected);
290290
const tests: InstrumentedTest[] = []
291291
tests.push(new InstrumentedTest().load(FixmeTests).setTestObject(new FixmeTests()));
292292
const val = await this.subject.executor.runTestsAsync(tests, "SERVER");
@@ -308,7 +308,7 @@ export public class TestExecutorTests {
308308
["fixme__multiScope___success", TestStatus.failed],
309309
["fixme__multiScope___fail", TestStatus.skipped_fixme],
310310
]
311-
const expectedMap = new Map<string, string>(expected as Iterable<Iterable2<string,string>>);
311+
const expectedMap = new Map<string, string>(expected);
312312
const tests: InstrumentedTest[] = []
313313
tests.push(new InstrumentedTest().load(FixmeTests).setTestObject(new FixmeTests()));
314314
const val = await this.subject.executor.runTestsAsync(tests, "MOCK");

n4js-libs/packages/org.eclipse.n4js.mangelhaft/src/n4js/org/eclipse/n4js/mangelhaft/InstrumentedTest.n4js

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export public class InstrumentedTest implements IInstrumentedTest {
155155
// Make shared TestController instance be accessible to test instances (via injection):
156156
const instance = testInjector.internalCreate(testClass, testInjector, new Map<string, N4Object>([
157157
[`${TestController.n4type.origin}${TestController.n4type.fqn}`, controller]
158-
] as Iterable<Iterable2<string, N4Object>>));
158+
]));
159159

160160
return new InstrumentedTest(testClass, info, instance, null, parameterizedTests);
161161
}

n4js-libs/packages/org.eclipse.n4js.mangelhaft/src/n4js/org/eclipse/n4js/mangelhaft/types/TestStatus.n4js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let testStatusIntMap = new Map<TestStatus, int>([
2828
, [TestStatus.failed, 6]
2929
, [TestStatus.error, 7]
3030

31-
] as Iterable<Iterable2<TestStatus,int>>);
31+
]);
3232

3333
export function aggregateTestStatuses(testStatus1: TestStatus, testStatus2: TestStatus): TestStatus {
3434
return testStatusIntMap.get(testStatus1) > testStatusIntMap.get(testStatus2) ? testStatus1 : testStatus2;

plugins/org.eclipse.n4js.ide/src/org/eclipse/n4js/ide/server/N4JSResourceTaskContext.java

+16
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@
1313
import java.util.HashMap;
1414
import java.util.Map;
1515

16+
import org.eclipse.emf.common.util.URI;
1617
import org.eclipse.n4js.resource.N4JSResource;
18+
import org.eclipse.n4js.scoping.builtin.BuiltInTypeScope;
1719
import org.eclipse.n4js.xtext.ide.server.ResourceTaskContext;
20+
import org.eclipse.n4js.xtext.ide.server.ResourceTaskManager;
21+
import org.eclipse.n4js.xtext.ide.server.util.XChunkedResourceDescriptions;
22+
import org.eclipse.n4js.xtext.workspace.WorkspaceConfigSnapshot;
1823
import org.eclipse.xtext.resource.XtextResource;
1924

2025
/**
@@ -29,4 +34,15 @@ public class N4JSResourceTaskContext extends ResourceTaskContext {
2934
return options;
3035
}
3136

37+
@Override
38+
public synchronized void initialize(
39+
ResourceTaskManager parent,
40+
URI uri,
41+
boolean isTemporary,
42+
XChunkedResourceDescriptions index,
43+
WorkspaceConfigSnapshot workspaceConfig) {
44+
45+
super.initialize(parent, uri, isTemporary, index, workspaceConfig);
46+
BuiltInTypeScope.get(getResourceSet()).getNullType(); // force loading built-ins to avoid deadlock later
47+
}
3248
}

plugins/org.eclipse.n4js.ide/src/org/eclipse/n4js/ide/server/symbol/N4JSDocumentSymbolService.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ public List<? extends Location> getDefinitions(XtextResource resource, int offse
109109
.eGet(N4JSPackage.eINSTANCE.getPropertyNameValuePair_Expression())) {
110110

111111
EObject elementInDestructuring = findReferenceHelper.getMemberInDestructuring(elemAtOffset);
112-
addLocations(resourceAccess, cancelIndicator, elementInDestructuring, locations);
112+
if (elementInDestructuring != null) {
113+
addLocations(resourceAccess, cancelIndicator, elementInDestructuring, locations);
114+
}
113115
}
114116

115117
addLocations(resourceAccess, cancelIndicator, element, locations);

plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/N4JSASTUtils.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -346,12 +346,17 @@ public static FunctionOrFieldAccessor getContainingFunctionOrAccessor(EObject eo
346346
* arrow function; otherwise <code>null</code> is returned.
347347
*/
348348
public static ArrowFunction getContainingSingleExpressionArrowFunction(Expression expression) {
349-
final EObject parent = expression.eContainer();
349+
EObject skippedParenthesesExpr = expression;
350+
EObject parent = expression.eContainer();
351+
while (parent instanceof ParenExpression) {
352+
skippedParenthesesExpr = parent;
353+
parent = parent.eContainer();
354+
}
350355
final EObject grandparent = parent != null ? parent.eContainer() : null;
351356
final EObject grandgrandparent = grandparent != null ? grandparent.eContainer() : null;
352357
if (grandgrandparent instanceof ArrowFunction) {
353358
final ArrowFunction arrFun = (ArrowFunction) grandgrandparent;
354-
if (arrFun.isSingleExprImplicitReturn() && arrFun.implicitReturnExpr() == expression) {
359+
if (arrFun.isSingleExprImplicitReturn() && arrFun.implicitReturnExpr() == skippedParenthesesExpr) {
355360
return arrFun;
356361
}
357362
}

plugins/org.eclipse.n4js.smith/src/org/eclipse/n4js/smith/N4JSDataCollectors.java

+5
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public final class N4JSDataCollectors {
7272
public static final DataCollector dcCacheMakeKeys = create("MakeKeys", dcCache);
7373
public static final DataCollector dcCacheSetTarget = create("SetTarget", dcCache);
7474

75+
public static final DataCollector dcInferenceContextSolve = create("Solve");
76+
public static final DataCollector dcInferenceContextReduce = create("Reduce", dcInferenceContextSolve);
77+
public static final DataCollector dcInferenceContextIncorporate = create("Incorporate", dcInferenceContextSolve);
78+
public static final DataCollector dcInferenceContextResolve = create("Resolve", dcInferenceContextSolve);
79+
7580
public static final DataCollector dcTypeHierachyTraverser = create("TypeHierachyTraverser");
7681
public static final DataCollector dcTHT_AllMembersCollector = create("AllMembersCollector",
7782
dcTypeHierachyTraverser);

plugins/org.eclipse.n4js.xtext.ide/src/org/eclipse/n4js/xtext/ide/server/TextDocumentFrontend.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,11 @@ protected List<? extends DocumentHighlight> documentHighlight(ResourceTaskContex
356356
}
357357
XtextResource res = rtc.getResource();
358358
XDocument doc = rtc.getDocument();
359-
return service.getDocumentHighlights(doc, res, params, cancelIndicator);
359+
try {
360+
return service.getDocumentHighlights(doc, res, params, cancelIndicator);
361+
} catch (Exception e) {
362+
return Collections.emptyList();
363+
}
360364
}
361365

362366
@Override

plugins/org.eclipse.n4js/src-env/env/builtin_js.n4jsd

+1-1
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ export external public class Array<T> {
377377
public toLocaleString(): string;
378378
public pop(): T;
379379
public push(...items: T): number;
380-
public concat(...items: union {T, Array<? extends T>}): Array<T>;
380+
public <S>concat(...items: union {S, Array<? extends S>}): Array<union{S,T}>;
381381
public join(separator: string = ): string;
382382
public reverse(): Array<T>;
383383
public shift(): T;

plugins/org.eclipse.n4js/src/org/eclipse/n4js/naming/N4JSImportedNamesAdapter.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@
1010
*/
1111
package org.eclipse.n4js.naming;
1212

13-
import java.util.Collections;
1413
import java.util.HashSet;
1514
import java.util.Iterator;
1615
import java.util.Set;
16+
import java.util.TreeSet;
1717

1818
import org.eclipse.xtext.linking.impl.ImportedNamesAdapter;
1919
import org.eclipse.xtext.naming.QualifiedName;
2020
import org.eclipse.xtext.resource.IEObjectDescription;
2121
import org.eclipse.xtext.scoping.IScope;
2222

2323
import com.google.common.base.Preconditions;
24+
import com.google.common.collect.Sets;
2425

2526
/**
2627
* Adapts default implementation to not normalize qualified names to be all lower case.
@@ -72,18 +73,18 @@ public IScope wrap(IScope scope) {
7273
}
7374

7475
@Override
75-
public Set<QualifiedName> getImportedNames() {
76-
return Collections.unmodifiableSet(nonNullImportedNames);
76+
synchronized public TreeSet<QualifiedName> getImportedNames() {
77+
return Sets.newTreeSet(nonNullImportedNames);
7778
}
7879

7980
/** Adds given element to set of imported names. */
80-
public void addImportedName(QualifiedName name) {
81+
synchronized public void addImportedName(QualifiedName name) {
8182
Preconditions.checkNotNull(name);
8283
nonNullImportedNames.add(name);
8384
}
8485

8586
@Override
86-
public void clear() {
87+
synchronized public void clear() {
8788
nonNullImportedNames.clear();
8889
}
8990
}

plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.java

+25
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
import static org.eclipse.xtext.xbase.lib.IterableExtensions.toSet;
1818
import static org.eclipse.xtext.xbase.lib.IteratorExtensions.exists;
1919

20+
import java.util.ArrayList;
21+
import java.util.Collections;
2022
import java.util.HashMap;
23+
import java.util.List;
2124
import java.util.Map;
2225
import java.util.Map.Entry;
2326

@@ -39,6 +42,7 @@
3942
import org.eclipse.n4js.n4JS.PropertyNameValuePair;
4043
import org.eclipse.n4js.n4JS.PropertySetterDeclaration;
4144
import org.eclipse.n4js.n4JS.PropertySpread;
45+
import org.eclipse.n4js.n4JS.ReturnStatement;
4246
import org.eclipse.n4js.ts.typeRefs.FunctionTypeExprOrRef;
4347
import org.eclipse.n4js.ts.typeRefs.TypeRef;
4448
import org.eclipse.n4js.ts.types.InferenceVariable;
@@ -56,6 +60,7 @@
5660
import org.eclipse.n4js.typesystem.utils.TypeSystemHelper;
5761
import org.eclipse.n4js.typesystem.utils.TypeSystemHelper.Callable;
5862
import org.eclipse.n4js.utils.N4JSLanguageUtils;
63+
import org.eclipse.xtext.xbase.lib.IteratorExtensions;
5964

6065
import com.google.inject.Inject;
6166

@@ -294,6 +299,26 @@ protected Map<InferenceVariable, TypeRef> createPseudoSolution(InferenceContext
294299
return pseudoSolution;
295300
}
296301

302+
protected List<Expression> getReturnExpressions(FunctionDefinition fun) {
303+
if (fun instanceof ArrowFunction && ((ArrowFunction) fun).isSingleExprImplicitReturn()) {
304+
Expression singleExpression = ((ArrowFunction) fun).getSingleExpression();
305+
if (singleExpression != null) {
306+
return Collections.singletonList(singleExpression);
307+
}
308+
}
309+
if (fun.getBody() != null) {
310+
List<ReturnStatement> returnStmts = IteratorExtensions.toList(fun.getBody().getAllReturnStatements());
311+
List<Expression> returnExpr = new ArrayList<>();
312+
for (ReturnStatement rs : returnStmts) {
313+
if (rs.getExpression() != null) {
314+
returnExpr.add(rs.getExpression());
315+
}
316+
}
317+
return returnExpr;
318+
}
319+
return Collections.emptyList();
320+
}
321+
297322
// FIXME move to a better place
298323
protected boolean isReturningValue(FunctionDefinition fun) {
299324
return (fun.getBody() != null && exists(fun.getBody().getAllReturnStatements(), s -> s.getExpression() != null))

plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/PolyProcessor.java

+16-5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
import org.eclipse.n4js.n4JS.Expression;
2121
import org.eclipse.n4js.n4JS.FormalParameter;
2222
import org.eclipse.n4js.n4JS.FunctionExpression;
23+
import org.eclipse.n4js.n4JS.N4JSASTUtils;
2324
import org.eclipse.n4js.n4JS.ObjectLiteral;
2425
import org.eclipse.n4js.n4JS.ParameterizedCallExpression;
2526
import org.eclipse.n4js.n4JS.PropertyAssignment;
2627
import org.eclipse.n4js.n4JS.PropertyMethodDeclaration;
2728
import org.eclipse.n4js.n4JS.RelationalExpression;
29+
import org.eclipse.n4js.n4JS.YieldExpression;
2830
import org.eclipse.n4js.ts.typeRefs.TypeRef;
2931
import org.eclipse.n4js.ts.types.TypableElement;
3032
import org.eclipse.n4js.ts.types.util.Variance;
@@ -161,14 +163,14 @@ void inferType(RuleEnvironment G, Expression rootPoly, ASTMetaInfoCache cache) {
161163
infCtx.addConstraint(TypeConstraint.FALSE);
162164
}
163165

164-
TypeRef expectedTypeOfPoly = destructureHelper.calculateExpectedType(rootPoly, G, infCtx);
166+
TypeRef expectedTypeByLiteralStructure = destructureHelper.calculateExpectedType(rootPoly, G, infCtx);
165167
// we have to pass the expected type to the #getType() method, so retrieve it first
166168
// (until the expectedType judgment is integrated into AST traversal, we have to invoke this judgment here;
167169
// in case of not-well-behaving expectedType rules, we use 'null' as expected type, i.e. no expectation)
168170
// TODO integrate expectedType judgment into AST traversal and remove #isProblematicCaseOfExpectedType()
169171
TypeRef expectedTypeRef = null;
170-
if (expectedTypeOfPoly != null) {
171-
expectedTypeRef = expectedTypeOfPoly;
172+
if (expectedTypeByLiteralStructure != null) {
173+
expectedTypeRef = expectedTypeByLiteralStructure;
172174
} else if (!isProblematicCaseOfExpectedType(rootPoly)) {
173175
expectedTypeRef = ts.expectedType(G, rootPoly.eContainer(), rootPoly);
174176
}
@@ -261,7 +263,16 @@ protected TypeRef processExpr(RuleEnvironment G, Expression expr, TypeRef expect
261263
* Returns true if we are not allowed to ask for the expected type of 'node', because this would lead to illegal
262264
* forward references (temporary).
263265
*/
264-
private boolean isProblematicCaseOfExpectedType(EObject node) {
265-
return node != null && node.eContainer() instanceof RelationalExpression;
266+
private boolean isProblematicCaseOfExpectedType(Expression node) {
267+
if (node != null) {
268+
EObject parent = N4JSASTUtils.skipParenExpressionUpward(node.eContainer());
269+
if (parent instanceof RelationalExpression) {
270+
return true;
271+
}
272+
if (parent instanceof YieldExpression) {
273+
return true;
274+
}
275+
}
276+
return false;
266277
}
267278
}

0 commit comments

Comments
 (0)