Skip to content

Commit 927d724

Browse files
authored
Merge pull request #1483 from markshannon/merge-121
Merge rc/1.21 into master
2 parents 59dd3b2 + 26f870b commit 927d724

File tree

28 files changed

+119
-68
lines changed

28 files changed

+119
-68
lines changed

change-notes/support/language-support.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ Note that where there are several versions or dialects of a language, the suppor
1717
.. [2] In addition, support is included for the preview features of C# 8.0 and .NET Core 3.0.
1818
.. [3] The best results are achieved with COBOL code that stays close to the ANSI 85 standard.
1919
.. [4] Builds that execute on Java 6 to 12 can be analyzed. The analysis understands Java 12 language features.
20-
.. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
21-
.. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
20+
.. [5] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
21+
.. [6] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
22+
.. [7] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Language,Variants,Compilers,Extensions
2-
C/C++,"C89, C99, C11, C++98, C++03, C++11, C++14, C++17","Clang extensions (up to Clang 8.0)
2+
C/C++,"C89, C99, C11, C++98, C++03, C++11, C++14, C++17","Clang extensions (up to Clang 8.0),
33
44
GNU extensions (up to GCC 8.3),
55
@@ -10,9 +10,9 @@ C#,C# up to 7.3. with .NET up to 4.8 [2]_.,"Microsoft Visual Studio up to 2019,
1010
1111
.NET Core up to 2.2","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
1212
COBOL,ANSI 85 or newer [3]_.,Not applicable,"``.cbl``, ``.CBL``, ``.cpy``, ``.CPY``, ``.copy``, ``.COPY``"
13-
Java,"Java 6 to 12 [4]_.","javac (OpenJDK and Oracle JDK)
13+
Java,"Java 6 to 12 [4]_.","javac (OpenJDK and Oracle JDK),
1414
15-
Eclipse compiler for Java (ECJ) batch compiler",``.java``
16-
JavaScript,ECMAScript 2019 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_."
15+
Eclipse compiler for Java (ECJ) [5]_.",``.java``
16+
JavaScript,ECMAScript 2019 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_."
1717
Python,"2.7, 3.5, 3.6, 3.7",Not applicable,``.py``
18-
TypeScript [6]_.,"2.6-3.5",Standard TypeScript compiler,"``.ts``, ``.tsx``"
18+
TypeScript [7]_.,"2.6-3.5",Standard TypeScript compiler,"``.ts``, ``.tsx``"

python/ql/src/semmle/python/Flow.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,7 @@ class BasicBlock extends @py_flow_node {
985985

986986
/** Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor
987987
* of `other` but does not strictly dominate `other` */
988+
pragma[noinline]
988989
predicate dominanceFrontier(BasicBlock other) {
989990
this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other)
990991
}

python/ql/src/semmle/python/Module.qll

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ class Module extends Module_, Scope, AstNode {
99
override string toString() {
1010
result = this.getKind() + " " + this.getName()
1111
or
12-
/* No name is defined, which means that this is not on an import path. So it must be a script */
12+
/* No name is defined, which means that this module is not on an import path. So it must be a script */
1313
not exists(this.getName()) and not this.isPackage() and
1414
result = "Script " + this.getFile().getShortName()
15+
or
16+
/* Package missing name, so just use the path instead */
17+
not exists(this.getName()) and this.isPackage() and
18+
result = "Package at " + this.getPath().getAbsolutePath()
1519
}
1620

1721
/** This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
@@ -67,9 +71,9 @@ class Module extends Module_, Scope, AstNode {
6771
string getAnExport() {
6872
py_exports(this, result)
6973
or
70-
exists(ModuleValue mod |
74+
exists(ModuleObjectInternal mod |
7175
mod.getSource() = this.getEntryNode() |
72-
mod.exports(result)
76+
mod.(ModuleValue).exports(result)
7377
)
7478
}
7579

@@ -191,6 +195,7 @@ class Module extends Module_, Scope, AstNode {
191195

192196
}
193197

198+
194199
bindingset[name]
195200
private predicate legalDottedName(string name) {
196201
name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*")
@@ -240,3 +245,30 @@ private predicate isStubRoot(Folder f) {
240245
f.getAbsolutePath().matches("%/data/python/stubs")
241246
}
242247

248+
249+
/** Holds if the Container `c` should be the preferred file or folder for
250+
* the given name when performing imports.
251+
* Trivially true for any container if it is the only one with its name.
252+
* However, if there are several modules with the same name, then
253+
* this is the module most likely to be imported under that name.
254+
*/
255+
predicate isPreferredModuleForName(Container c, string name) {
256+
exists(int p |
257+
p = min(int x | x = priorityForName(_, name)) and
258+
p = priorityForName(c, name)
259+
)
260+
}
261+
262+
private int priorityForName(Container c, string name) {
263+
name = moduleNameFromFile(c) and
264+
(
265+
// In the source
266+
exists(c.getRelativePath()) and result = -1
267+
or
268+
// On an import path
269+
exists(c.getImportRoot(result))
270+
or
271+
// Otherwise
272+
result = 10000
273+
)
274+
}

python/ql/src/semmle/python/objects/Callables.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ private import semmle.python.types.Builtins
1111

1212
abstract class CallableObjectInternal extends ObjectInternal {
1313

14-
/** Gets the name of this callable */
15-
abstract string getName();
16-
1714
/** Gets the scope of this callable if it has one */
1815
abstract Function getScope();
1916

python/ql/src/semmle/python/objects/Constants.qll

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,6 @@ abstract class ConstantObjectInternal extends ObjectInternal {
7373

7474
private abstract class BooleanObjectInternal extends ConstantObjectInternal {
7575

76-
BooleanObjectInternal() {
77-
this = TTrue() or this = TFalse()
78-
}
79-
80-
8176
override ObjectInternal getClass() {
8277
result = TBuiltinClassObject(Builtin::special("bool"))
8378
}

python/ql/src/semmle/python/objects/Instances.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,16 +440,21 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
440440
pragma [noinline] override predicate descriptorGetInstance(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) { none() }
441441

442442
pragma [noinline] override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) {
443-
PointsToInternal::attributeRequired(this, name) and
444443
exists(ObjectInternal cls_attr, CfgOrigin attr_orig |
445-
this.lookup(name, cls_attr, attr_orig)
444+
this.attribute_descriptor(name, cls_attr, attr_orig)
446445
|
447446
cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig
448447
or
449448
cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this.getSelf(), value, origin)
450449
)
451450
}
452451

452+
/* Helper for `attribute` */
453+
pragma [noinline] private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) {
454+
PointsToInternal::attributeRequired(this, name) and
455+
this.lookup(name, cls_attr, attr_orig)
456+
}
457+
453458
private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) {
454459
Types::getMro(this.getSelf().getClass()).startingAt(this.getStartClass()).getTail().lookup(name, value, origin)
455460
}

python/ql/src/semmle/python/objects/ObjectAPI.qll

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,6 @@ class Value extends TObject {
7171
this.(ObjectInternal).attribute(name, result, _)
7272
}
7373

74-
/** DEPRECATED: For backwards compatibility with old API
75-
* Use `Value` instead of `ObjectSource`.
76-
*/
77-
deprecated ObjectSource getSource() {
78-
result = this.(ObjectInternal).getSource()
79-
}
80-
8174
/** Holds if this value is builtin. Applies to built-in functions and methods,
8275
* but also integers and strings.
8376
*/
@@ -153,9 +146,9 @@ module Module {
153146
*
154147
* Note that the name used to refer to a module is not
155148
* necessarily its name. For example,
156-
* there are modules refered to by the name `os.path`,
149+
* there are modules referred to by the name `os.path`,
157150
* but that are not named `os.path`, for example the module `posixpath`.
158-
* Such that the follwing is true:
151+
* Such that the following is true:
159152
* `Module::named("os.path").getName() = "posixpath"
160153
*/
161154
ModuleValue named(string name) {

python/ql/src/semmle/python/objects/TObject.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ cached newtype TObject =
4747
or
4848
/* Package objects */
4949
TPackageObject(Folder f) {
50-
exists(moduleNameFromFile(f))
50+
isPreferredModuleForName(f, _)
5151
}
5252
or
5353
/* Python module objects */
5454
TPythonModule(Module m) {
55-
not m.isPackage() and not exists(SyntaxError se | se.getFile() = m.getFile())
55+
not m.isPackage() and isPreferredModuleForName(m.getFile(), _) and
56+
not exists(SyntaxError se | se.getFile() = m.getFile())
5657
}
5758
or
5859
/* `True` */

python/ql/src/semmle/python/pointsto/PointsTo.qll

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ module PointsTo {
141141
)
142142
or
143143
not f.isParameter() and
144-
exists(Value value |
144+
exists(ObjectInternal value |
145145
PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and
146146
cls = value.getClass().getSource() |
147147
obj = value.getSource() or
@@ -151,7 +151,7 @@ module PointsTo {
151151

152152
deprecated predicate
153153
ssa_variable_points_to(EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin) {
154-
exists(Value value |
154+
exists(ObjectInternal value |
155155
PointsToInternal::variablePointsTo(var, context, value, origin) and
156156
cls = value.getClass().getSource() |
157157
obj = value.getSource()
@@ -160,8 +160,8 @@ module PointsTo {
160160

161161
deprecated
162162
CallNode get_a_call(Object func, PointsToContext context) {
163-
exists(Value value |
164-
result = value.getACall(context) and
163+
exists(ObjectInternal value |
164+
result = value.(Value).getACall(context) and
165165
func = value.getSource()
166166
)
167167
}
@@ -402,7 +402,7 @@ cached module PointsToInternal {
402402
or
403403
scope_entry_points_to(def, context, value, origin)
404404
or
405-
InterModulePointsTo::implicit_submodule_points_to(def, context, value, origin)
405+
InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport()
406406
or
407407
iteration_definition_points_to(def, context, value, origin)
408408
/*
@@ -647,18 +647,22 @@ private module InterModulePointsTo {
647647
)
648648
}
649649

650+
/* Helper for implicit_submodule_points_to */
651+
private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) {
652+
exists(PackageObjectInternal package |
653+
package.getSourceModule() = def.getDefiningNode().getScope() and
654+
result = package.submodule(def.getSourceVariable().getName())
655+
)
656+
}
657+
650658
/** Implicit "definition" of the names of submodules at the start of an `__init__.py` file.
651659
*
652660
* PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically.
653661
*/
654662
pragma [noinline]
655-
predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, ModuleObjectInternal value, ControlFlowNode origin) {
656-
exists(PackageObjectInternal package |
657-
package.getSourceModule() = def.getDefiningNode().getScope() |
658-
value = package.submodule(def.getSourceVariable().getName()) and
659-
origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) and
660-
context.isImport()
661-
)
663+
predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin) {
664+
value = getModule(def) and
665+
origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode())
662666
}
663667

664668
/** Points-to for `from ... import *`. */
@@ -1869,9 +1873,13 @@ cached module Types {
18691873
result = getInheritedMetaclass(cls, 0)
18701874
or
18711875
// Best guess if base is not a known class
1876+
hasUnknownBase(cls) and result = ObjectInternal::unknownClass()
1877+
}
1878+
1879+
/* Helper for getInheritedMetaclass */
1880+
private predicate hasUnknownBase(ClassObjectInternal cls) {
18721881
exists(ObjectInternal base |
1873-
base = getBase(cls, _) and
1874-
result = ObjectInternal::unknownClass() |
1882+
base = getBase(cls, _) |
18751883
base.isClass() = false
18761884
or
18771885
base = ObjectInternal::unknownClass()
@@ -1881,14 +1889,18 @@ cached module Types {
18811889
private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) {
18821890
exists(Class c |
18831891
c = cls.(PythonClassObjectInternal).getScope() and
1884-
n = count(c.getABase())
1892+
n = count(c.getABase()) and n != 1
18851893
|
18861894
result = ObjectInternal::type() and major_version() = 3
18871895
or
18881896
result = ObjectInternal::classType() and major_version() = 2
18891897
)
18901898
or
1899+
base_count(cls) = 1 and n = 0 and
1900+
result = getBase(cls, 0).getClass()
1901+
or
18911902
exists(ClassObjectInternal meta1, ClassObjectInternal meta2 |
1903+
base_count(cls) > 1 and
18921904
meta1 = getBase(cls, n).getClass() and
18931905
meta2 = getInheritedMetaclass(cls, n+1)
18941906
|

0 commit comments

Comments
 (0)