|
16 | 16 | */
|
17 | 17 | package org.sonar.python.tree;
|
18 | 18 |
|
| 19 | +import static org.assertj.core.api.Assertions.assertThat; |
| 20 | +import static org.assertj.core.api.Assertions.assertThatThrownBy; |
| 21 | +import static org.mockito.Mockito.mock; |
| 22 | +import static org.mockito.Mockito.when; |
| 23 | +import static org.sonar.python.PythonTestUtils.lastExpression; |
| 24 | +import static org.sonar.python.PythonTestUtils.pythonFile; |
| 25 | + |
19 | 26 | import com.sonar.sslr.api.AstNode;
|
20 | 27 | import java.util.ArrayList;
|
21 | 28 | import java.util.Arrays;
|
|
30 | 37 | import org.sonar.plugins.python.api.tree.ClassDef;
|
31 | 38 | import org.sonar.plugins.python.api.tree.DottedName;
|
32 | 39 | import org.sonar.plugins.python.api.tree.Expression;
|
| 40 | +import org.sonar.plugins.python.api.tree.ExpressionStatement; |
33 | 41 | import org.sonar.plugins.python.api.tree.FileInput;
|
34 | 42 | import org.sonar.plugins.python.api.tree.FunctionDef;
|
35 | 43 | import org.sonar.plugins.python.api.tree.HasSymbol;
|
|
44 | 52 | import org.sonar.plugins.python.api.tree.Tree;
|
45 | 53 | import org.sonar.plugins.python.api.tree.Tree.Kind;
|
46 | 54 | import org.sonar.plugins.python.api.tree.WhileStatement;
|
| 55 | +import org.sonar.plugins.python.api.types.v2.ClassType; |
| 56 | +import org.sonar.plugins.python.api.types.v2.FunctionType; |
| 57 | +import org.sonar.plugins.python.api.types.v2.ObjectType; |
| 58 | +import org.sonar.plugins.python.api.types.v2.PythonType; |
47 | 59 | import org.sonar.python.PythonTestUtils;
|
48 | 60 | import org.sonar.python.api.PythonTokenType;
|
49 | 61 | import org.sonar.python.parser.PythonParser;
|
50 | 62 | import org.sonar.python.semantic.SymbolTableBuilder;
|
51 |
| - |
52 |
| -import static org.assertj.core.api.Assertions.assertThat; |
53 |
| -import static org.assertj.core.api.Assertions.assertThatThrownBy; |
54 |
| -import static org.sonar.python.PythonTestUtils.lastExpression; |
55 |
| -import static org.sonar.python.PythonTestUtils.pythonFile; |
| 63 | +import org.sonar.python.types.v2.TypesTestUtils; |
56 | 64 |
|
57 | 65 | class TreeUtilsTest {
|
58 | 66 |
|
@@ -726,6 +734,115 @@ void test_stringValueFromNameOrQualifiedExpression() {
|
726 | 734 | assertThat(TreeUtils.stringValueFromNameOrQualifiedExpression(expression)).isEmpty();
|
727 | 735 | }
|
728 | 736 |
|
| 737 | + |
| 738 | + @Test |
| 739 | + void testGetTypeOfSingleAssignedName() { |
| 740 | + var tree = TypesTestUtils.parseAndInferTypes(""" |
| 741 | + a = 1 |
| 742 | + def foo(): |
| 743 | + a |
| 744 | + """); |
| 745 | + |
| 746 | + FunctionDef functionDef = PythonTestUtils.getFirstChild(tree, t -> t.is(Tree.Kind.FUNCDEF)); |
| 747 | + Name name = PythonTestUtils.getFirstChild(functionDef.body(), t -> t.is(Tree.Kind.NAME)); |
| 748 | + |
| 749 | + assertThat(name.name()).isEqualTo("a"); |
| 750 | + |
| 751 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(name)) |
| 752 | + .isInstanceOfSatisfying(ObjectType.class, nameType -> |
| 753 | + assertThat(nameType.unwrappedType()).isSameAs(TypesTestUtils.INT_TYPE)); |
| 754 | + } |
| 755 | + |
| 756 | + @Test |
| 757 | + void testGetTypeOfSingleAssignedName_qualifiedExpr() { |
| 758 | + var tree = TypesTestUtils.parseAndInferTypes(""" |
| 759 | + import requests |
| 760 | + session = requests.Session() |
| 761 | + def foo(): |
| 762 | + session.get |
| 763 | + session.cookies.copy |
| 764 | + Session().get |
| 765 | + requests.get |
| 766 | + requests.get() |
| 767 | + """); |
| 768 | + |
| 769 | + FunctionDef functionDef = PythonTestUtils.getFirstChild(tree, t -> t.is(Tree.Kind.FUNCDEF)); |
| 770 | + List<Expression> expressions = functionDef.body().statements().stream() |
| 771 | + .map(statement -> ((ExpressionStatement) statement).expressions().get(0)) |
| 772 | + .toList(); |
| 773 | + |
| 774 | + |
| 775 | + QualifiedExpression sessionGetExpr = (QualifiedExpression) expressions.get(0); |
| 776 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(sessionGetExpr)) |
| 777 | + .isInstanceOfSatisfying(FunctionType.class, functionType -> |
| 778 | + assertThat(functionType.name()).isEqualTo("get")); |
| 779 | + |
| 780 | + QualifiedExpression cookiesCopyExpr = (QualifiedExpression) expressions.get(1); |
| 781 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(cookiesCopyExpr)) |
| 782 | + .isInstanceOfSatisfying(FunctionType.class, functionType -> |
| 783 | + assertThat(functionType.name()).isEqualTo("copy")); |
| 784 | + |
| 785 | + QualifiedExpression sessionInstanceGet = (QualifiedExpression) expressions.get(2); |
| 786 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(sessionInstanceGet)) |
| 787 | + .isSameAs(PythonType.UNKNOWN); |
| 788 | + |
| 789 | + QualifiedExpression requestGetExpr = (QualifiedExpression) expressions.get(3); |
| 790 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(requestGetExpr)) |
| 791 | + .isInstanceOfSatisfying(FunctionType.class, functionType -> |
| 792 | + assertThat(functionType.name()).isEqualTo("get")); |
| 793 | + |
| 794 | + CallExpression requestsGetCallExpr = (CallExpression) expressions.get(4); |
| 795 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(requestsGetCallExpr)) |
| 796 | + .isInstanceOf(ObjectType.class) |
| 797 | + .extracting(PythonType::unwrappedType) |
| 798 | + .isInstanceOfSatisfying(ClassType.class, classType -> |
| 799 | + assertThat(classType.name()).isEqualTo("Response")); |
| 800 | + } |
| 801 | + |
| 802 | + |
| 803 | + @Test |
| 804 | + void testGetTypeOfSingleAssignedName_multipleAssignment() { |
| 805 | + var tree = TypesTestUtils.parseAndInferTypes(""" |
| 806 | + a = 1 |
| 807 | + def foo(): |
| 808 | + a |
| 809 | + a = 12 |
| 810 | + """); |
| 811 | + |
| 812 | + FunctionDef functionDef = PythonTestUtils.getFirstChild(tree, t -> t.is(Tree.Kind.FUNCDEF)); |
| 813 | + Name name = PythonTestUtils.getFirstChild(functionDef.body(), t -> t.is(Tree.Kind.NAME)); |
| 814 | + |
| 815 | + assertThat(name.name()).isEqualTo("a"); |
| 816 | + |
| 817 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(name)) |
| 818 | + .isSameAs(PythonType.UNKNOWN); |
| 819 | + } |
| 820 | + |
| 821 | + @Test |
| 822 | + void testGetTypeOfSingleAssignedName_sameScope() { |
| 823 | + var tree = TypesTestUtils.parseAndInferTypes(""" |
| 824 | + a = 1 |
| 825 | + a |
| 826 | + """); |
| 827 | + |
| 828 | + Name name = PythonTestUtils.getLastDescendant(tree, t -> t.is(Tree.Kind.NAME)); |
| 829 | + assertThat(name.name()).isEqualTo("a"); |
| 830 | + |
| 831 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(name)) |
| 832 | + .isInstanceOfSatisfying(ObjectType.class, nameType -> |
| 833 | + assertThat(nameType.unwrappedType()).isSameAs(TypesTestUtils.INT_TYPE)); |
| 834 | + } |
| 835 | + |
| 836 | + @Test |
| 837 | + void testGetTypeOfSingleAssignedName_noSymbol() { |
| 838 | + var name = mock(Name.class); |
| 839 | + when(name.symbolV2()).thenReturn(null); |
| 840 | + when(name.typeV2()).thenReturn(PythonType.UNKNOWN); |
| 841 | + |
| 842 | + assertThat(TreeUtils.inferSingleAssignedExpressionType(name)) |
| 843 | + .isSameAs(PythonType.UNKNOWN); |
| 844 | + } |
| 845 | + |
729 | 846 | private static boolean isOuterFunction(Tree tree) {
|
730 | 847 | return tree.is(Kind.FUNCDEF) && ((FunctionDef) tree).name().name().equals("outer");
|
731 | 848 | }
|
|
0 commit comments