Skip to content

Commit 18db8e6

Browse files
SONARPY-1797 Introduce new type inference model classes
1 parent c2ab0f4 commit 18db8e6

File tree

11 files changed

+367
-0
lines changed

11 files changed

+367
-0
lines changed

python-frontend/src/main/java/org/sonar/plugins/python/api/tree/Expression.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@
2222
import com.google.common.annotations.Beta;
2323
import org.sonar.plugins.python.api.types.InferredType;
2424
import org.sonar.python.types.InferredTypes;
25+
import org.sonar.python.types.v2.PythonType;
2526

2627
public interface Expression extends Tree {
2728

2829
@Beta
2930
default InferredType type() {
3031
return InferredTypes.anyType();
3132
}
33+
34+
@Beta
35+
default PythonType pythonType() {
36+
return PythonType.UNKNOWN;
37+
}
3238

3339
}

python-frontend/src/main/java/org/sonar/plugins/python/api/tree/QualifiedExpression.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.annotation.CheckForNull;
2323
import org.sonar.plugins.python.api.symbols.Symbol;
2424
import org.sonar.plugins.python.api.symbols.Usage;
25+
import org.sonar.python.types.v2.PythonType;
2526

2627
/**
2728
* Qualified expression like "foo.bar"
@@ -56,4 +57,9 @@ default Symbol symbol() {
5657
default Usage usage() {
5758
return name().usage();
5859
}
60+
61+
@Override
62+
default PythonType pythonType() {
63+
return name().pythonType();
64+
}
5965
}

python-frontend/src/main/java/org/sonar/python/tree/NameImpl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.sonar.plugins.python.api.tree.TreeVisitor;
3232
import org.sonar.plugins.python.api.types.InferredType;
3333
import org.sonar.python.types.InferredTypes;
34+
import org.sonar.python.types.v2.PythonType;
3435

3536
public class NameImpl extends PyTree implements Name {
3637
private final Token token;
@@ -39,6 +40,7 @@ public class NameImpl extends PyTree implements Name {
3940
private Symbol symbol;
4041
private Usage usage;
4142
private InferredType inferredType = InferredTypes.anyType();
43+
private PythonType pythonType = PythonType.UNKNOWN;
4244
private static final String TRUE = "True";
4345
private static final String FALSE = "False";
4446

@@ -118,4 +120,14 @@ private boolean isBooleanBuiltinSymbol() {
118120
public void setInferredType(InferredType inferredType) {
119121
this.inferredType = inferredType;
120122
}
123+
124+
@Override
125+
public PythonType pythonType() {
126+
return pythonType;
127+
}
128+
129+
public NameImpl pythonType(PythonType pythonType) {
130+
this.pythonType = pythonType;
131+
return this;
132+
}
121133
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import java.util.ArrayList;
23+
import java.util.Collection;
24+
import java.util.List;
25+
import java.util.Objects;
26+
import java.util.Optional;
27+
import java.util.stream.Collectors;
28+
29+
/**
30+
* ClassType
31+
*/
32+
public record ClassType(
33+
String name,
34+
List<Member> members,
35+
List<PythonType> attributes,
36+
List<PythonType> superClasses,
37+
List<PythonType> typeVars) implements PythonType {
38+
39+
public ClassType(String name) {
40+
this(name, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
41+
}
42+
43+
public ClassType(String name, List<PythonType> attributes) {
44+
this(name, new ArrayList<>(), attributes, new ArrayList<>(), new ArrayList<>());
45+
}
46+
47+
@Override
48+
public String displayName() {
49+
var splits = name.split("\\.");
50+
if (splits.length > 0) {
51+
return splits[splits.length - 1];
52+
}
53+
return name;
54+
}
55+
56+
@Override
57+
public boolean isCompatibleWith(PythonType another) {
58+
if (another instanceof ObjectType) {
59+
return this.isCompatibleWith(((ObjectType) another).type());
60+
}
61+
if (another instanceof UnionType) {
62+
return ((UnionType) another).candidates().stream().anyMatch(c -> this.isCompatibleWith(c));
63+
}
64+
if (another instanceof FunctionType) {
65+
return this.isCompatibleWith(((FunctionType) another).returnType());
66+
}
67+
if (another instanceof ClassType) {
68+
var other = (ClassType) another;
69+
var isASubClass = this.isASubClassFrom(other);
70+
var areAttributeCompatible = this.areAttributesCompatible(other);
71+
var isDuckTypeCompatible = !this.members.isEmpty() && other.members.stream().allMatch(member -> this.members.contains(member));
72+
return Objects.equals(this, another) || "builtins.object".equals(other.name()) ||
73+
isDuckTypeCompatible ||
74+
( isASubClass && areAttributeCompatible) ;
75+
}
76+
return true;
77+
}
78+
79+
public boolean isASubClassFrom(ClassType other) {
80+
return superClasses.stream().anyMatch(superClass -> superClass.isCompatibleWith(other));
81+
}
82+
83+
public boolean areAttributesCompatible(ClassType other) {
84+
return attributes.stream().allMatch(attr -> other.attributes.stream().anyMatch(otherAttr -> attr.isCompatibleWith(otherAttr)));
85+
}
86+
87+
@Override
88+
public String key() {
89+
return Optional.of(attributes())
90+
.stream()
91+
.flatMap(Collection::stream)
92+
.map(PythonType::key)
93+
.collect(Collectors.joining(",", name() + "[", "]"));
94+
}
95+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import java.util.List;
23+
24+
/**
25+
* FunctionType
26+
*/
27+
public record FunctionType(
28+
String name,
29+
List<Member> members,
30+
List<PythonType> attributes,
31+
List<PythonType> typeVars,
32+
List<Member> parameters,
33+
PythonType returnType) implements PythonType {
34+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
public record Member(String name, PythonType type) {
23+
}
24+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import java.util.List;
23+
import java.util.Optional;
24+
25+
public record ModuleType(String name, List<PythonType> children) implements PythonType {
26+
27+
@Override
28+
public boolean isCompatibleWith(PythonType another) {
29+
return Optional.ofNullable(another)
30+
.filter(ModuleType.class::isInstance)
31+
.map(ModuleType.class::cast)
32+
.map(ModuleType::name)
33+
.filter(name::equals)
34+
.isPresent();
35+
}
36+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import java.util.List;
23+
24+
public record ObjectType(PythonType type, List<PythonType> attributes, List<Member> members) implements PythonType {
25+
26+
@Override
27+
public String displayName() {
28+
return type.displayName();
29+
}
30+
31+
@Override
32+
public boolean isCompatibleWith(PythonType another) {
33+
return this.type.isCompatibleWith(another);
34+
}
35+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
/**
23+
* PythonType
24+
*/
25+
public interface PythonType {
26+
PythonType UNKNOWN = new UnknownType();
27+
28+
default String name() {
29+
return this.toString();
30+
}
31+
32+
default String displayName() {
33+
return this.toString();
34+
}
35+
36+
default boolean isCompatibleWith(PythonType another) {
37+
return true;
38+
}
39+
40+
default String key() {
41+
return name();
42+
}
43+
44+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import java.util.List;
23+
24+
public record UnionType(List<PythonType> candidates) implements PythonType {
25+
public UnionType() {
26+
this(List.of());
27+
}
28+
29+
@Override
30+
public String displayName() {
31+
var candidatesName = candidates.stream().map(c -> c.displayName()).toList();
32+
return "Union[%s]".formatted(String.join(", ", candidatesName));
33+
}
34+
35+
@Override
36+
public boolean isCompatibleWith(PythonType another) {
37+
return candidates.isEmpty() || candidates.stream()
38+
.anyMatch(candidate -> candidate.isCompatibleWith(another));
39+
}
40+
}

0 commit comments

Comments
 (0)