Skip to content

Commit

Permalink
Fix for #863: respect declared type of variable for STC (incl. if/else)
Browse files Browse the repository at this point in the history
GROOVY-9064
  • Loading branch information
eric-milles committed Apr 4, 2019
1 parent e29dba0 commit 7eaf200
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright 2009-2019 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.
* You may obtain a copy of the License at
*
* https://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.
*/
package org.eclipse.jdt.core.groovy.tests.search;

import static org.eclipse.jdt.groovy.core.tests.GroovyBundle.isAtLeastGroovy;
import static org.junit.Assume.assumeTrue;

import org.junit.Before;
import org.junit.Test;

public final class Groovy25InferencingTests extends InferencingTestSuite {

@Before
public void setUp() {
assumeTrue(isAtLeastGroovy(25));
}

@Test
public void testCompileStaticVariableAssignment1() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth() {\n" +
" List list = Collections.emptyList()\n" +
"}";

String target = "list";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.List");
}

@Test
public void testCompileStaticVariableAssignment2() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth() {\n" +
" List list = new ArrayList()\n" +
"}";

String target = "list";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.List");
}

@Test
public void testCompileStaticVariableAssignment3() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth() {\n" +
" List list = [1, 2]\n" +
"}";

String target = "list";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.List");
}

@Test
public void testCompileStaticVariableAssignment4() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth(boolean flag) {\n" +
" List list = [1, 2]\n" +
" if (flag) {\n" +
" list = [3, 'four']\n" +
" }\n" +
"}";

String target = "list";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.List");
}

@Test
public void testCompileStaticVariableAssignment5() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth() {\n" +
" Map map = Collections.emptyMap()\n" +
"}";

String target = "map";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.Map");
}

@Test
public void testCompileStaticVariableAssignment6() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth() {\n" +
" Map map = new HashMap()\n" +
"}";

String target = "map";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.Map");
}

@Test
public void testCompileStaticVariableAssignment7() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth() {\n" +
" Map map = [:]\n" +
"}";

String target = "map";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.Map");
}

@Test
public void testCompileStaticVariableAssignment8() {
String contents =
"@groovy.transform.CompileStatic\n" +
"void meth(boolean flag) {\n" +
" Map map = [:]\n" +
" if (flag) {\n" +
" map = [a: 1, b: '2']\n" +
" }\n" +
"}";

String target = "map";
int offset = contents.indexOf(target);
assertType(contents, offset, offset + target.length(), "java.util.Map");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -855,22 +855,22 @@ public void visitBinaryExpression(BinaryExpression expression) {
if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
// unchecked assignment
// examples:
// List<A> list = new LinkedList()
// List<A> list = []
// List<A> list = new LinkedList()
// Iterable<A> list = new LinkedList()

// in that case, the inferred type of the binary expression is the type of the RHS
// "completed" with generics type information available in the LHS
ClassNode completedType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());

resultType = completedType;

}
if (isArrayOp(op) &&
enclosingBinaryExpression != null

if (isArrayOp(op)
&& !lType.isArray()
&& enclosingBinaryExpression != null
&& enclosingBinaryExpression.getLeftExpression() == expression
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
&& !lType.isArray()) {
&& isAssignment(enclosingBinaryExpression.getOperation().getType())) {
// left hand side of an assignment : map['foo'] = ...
Expression enclosingBE_rightExpr = enclosingBinaryExpression.getRightExpression();
if (!(enclosingBE_rightExpr instanceof ClosureExpression)) {
Expand All @@ -884,6 +884,7 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())
addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
}
}

boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression;
if (!isEmptyDeclaration && isAssignment(op)) {
if (rightExpression instanceof ConstructorCallExpression) {
Expand Down Expand Up @@ -2945,7 +2946,6 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
}
}
boolean lastArg = (i == length - 1);

if (lastArg && inferredType.isArray()) {
if (/*GRECLIPSE add -- GROOVY-9058*/!closureParam.isDynamicTyped() && /*GRECLIPSE end*/inferredType.getComponentType().equals(originType)) {
inferredType = originType;
Expand Down Expand Up @@ -3860,6 +3860,9 @@ protected Map<VariableExpression, ClassNode> popAssignmentTracking(final Map<Var
if (!typeCheckingContext.ifElseForWhileAssignmentTracker.isEmpty()) {
for (Map.Entry<VariableExpression, List<ClassNode>> entry : typeCheckingContext.ifElseForWhileAssignmentTracker.entrySet()) {
VariableExpression key = entry.getKey();
// GRECLIPSE add -- GROOVY-9064
if (!key.isDynamicTyped()) continue;
// GRECLIPSE end
List<ClassNode> allValues = entry.getValue();
// GROOVY-6099: First element of the list may be null, if no assignment was made before the branch
List<ClassNode> nonNullValues = new ArrayList<ClassNode>(allValues.size());
Expand Down Expand Up @@ -4077,7 +4080,7 @@ protected void storeType(Expression exp, ClassNode cn) {
VariableExpression var = (VariableExpression) exp;
final Variable accessedVariable = var.getAccessedVariable();
if (accessedVariable != exp && accessedVariable instanceof VariableExpression) {
storeType((Expression) accessedVariable, cn);
storeType((VariableExpression) accessedVariable, cn);
}
if (accessedVariable instanceof Parameter) {
((Parameter) accessedVariable).putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, cn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -849,22 +849,22 @@ public void visitBinaryExpression(BinaryExpression expression) {
if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
// unchecked assignment
// examples:
// List<A> list = new LinkedList()
// List<A> list = []
// List<A> list = new LinkedList()
// Iterable<A> list = new LinkedList()

// in that case, the inferred type of the binary expression is the type of the RHS
// "completed" with generics type information available in the LHS
ClassNode completedType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());

resultType = completedType;

}
if (isArrayOp(op) &&
enclosingBinaryExpression != null

if (isArrayOp(op)
&& !lType.isArray()
&& enclosingBinaryExpression != null
&& enclosingBinaryExpression.getLeftExpression() == expression
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
&& !lType.isArray()) {
&& isAssignment(enclosingBinaryExpression.getOperation().getType())) {
// left hand side of an assignment : map['foo'] = ...
Expression enclosingBE_rightExpr = enclosingBinaryExpression.getRightExpression();
if (!(enclosingBE_rightExpr instanceof ClosureExpression)) {
Expand All @@ -878,6 +878,7 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())
addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
}
}

boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression;
if (!isEmptyDeclaration && isAssignment(op)) {
if (rightExpression instanceof ConstructorCallExpression) {
Expand Down Expand Up @@ -3960,6 +3961,9 @@ protected Map<VariableExpression, ClassNode> popAssignmentTracking(final Map<Var
if (!typeCheckingContext.ifElseForWhileAssignmentTracker.isEmpty()) {
for (Map.Entry<VariableExpression, List<ClassNode>> entry : typeCheckingContext.ifElseForWhileAssignmentTracker.entrySet()) {
VariableExpression key = entry.getKey();
// GRECLIPSE add -- GROOVY-9064
if (!key.isDynamicTyped()) continue;
// GRECLIPSE end
List<ClassNode> allValues = entry.getValue();
// GROOVY-6099: First element of the list may be null, if no assignment was made before the branch
List<ClassNode> nonNullValues = new ArrayList<ClassNode>(allValues.size());
Expand Down Expand Up @@ -4177,7 +4181,7 @@ protected void storeType(Expression exp, ClassNode cn) {
VariableExpression var = (VariableExpression) exp;
final Variable accessedVariable = var.getAccessedVariable();
if (accessedVariable != exp && accessedVariable instanceof VariableExpression) {
storeType((Expression) accessedVariable, cn);
storeType((VariableExpression) accessedVariable, cn);
}
if (accessedVariable instanceof Parameter) {
((Parameter) accessedVariable).putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, cn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 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
* https://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,
Expand Down Expand Up @@ -82,6 +82,7 @@ import org.junit.runners.Suite
org.eclipse.jdt.core.groovy.tests.search.GenericsMappingTests,
org.eclipse.jdt.core.groovy.tests.search.Groovy20InferencingTests,
org.eclipse.jdt.core.groovy.tests.search.Groovy21InferencingTests,
org.eclipse.jdt.core.groovy.tests.search.Groovy25InferencingTests,
org.eclipse.jdt.core.groovy.tests.search.InferencingTests,
org.eclipse.jdt.core.groovy.tests.search.JDTPropertyNodeInferencingTests,
org.eclipse.jdt.core.groovy.tests.search.LocalVariableReferenceSearchTests,
Expand Down

0 comments on commit 7eaf200

Please sign in to comment.