diff --git a/python/ql/lib/LegacyPointsTo.qll b/python/ql/lib/LegacyPointsTo.qll index ed04d9e9e79..039db7bafff 100644 --- a/python/ql/lib/LegacyPointsTo.qll +++ b/python/ql/lib/LegacyPointsTo.qll @@ -438,7 +438,7 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics { * One callable "this" depends on another callable "result" * if "this" makes some call to a method that may end up being "result". */ - FunctionMetricsWithPointsTo getADependency() { + override FunctionMetricsWithPointsTo getADependency() { result != this and not non_coupling_method(result) and exists(Call call | call.getScope() = this | @@ -462,7 +462,7 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics { * the number of callables that depend on this method. * This is sometimes called the "fan-in" of a method. */ - int getAfferentCoupling() { + override int getAfferentCoupling() { result = count(FunctionMetricsWithPointsTo m | m.getADependency() = this) } @@ -471,7 +471,7 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics { * the number of methods that this method depends on * This is sometimes called the "fan-out" of a method. */ - int getEfferentCoupling() { + override int getEfferentCoupling() { result = count(FunctionMetricsWithPointsTo m | this.getADependency() = m) } @@ -500,13 +500,17 @@ class ClassMetricsWithPointsTo extends ClassMetrics { * Gets the afferent coupling of a class -- the number of classes that * directly depend on it. */ - int getAfferentCoupling() { result = count(ClassMetricsWithPointsTo t | t.dependsOn(this)) } + override int getAfferentCoupling() { + result = count(ClassMetricsWithPointsTo t | t.dependsOn(this)) + } /** * Gets the efferent coupling of a class -- the number of classes that * it directly depends on. */ - int getEfferentCoupling() { result = count(ClassMetricsWithPointsTo t | this.dependsOn(t)) } + override int getEfferentCoupling() { + result = count(ClassMetricsWithPointsTo t | this.dependsOn(t)) + } /** Gets the depth of inheritance of the class. */ int getInheritanceDepth() { @@ -537,13 +541,17 @@ class ModuleMetricsWithPointsTo extends ModuleMetrics { * Gets the afferent coupling of a module -- the number of modules that * directly depend on it. */ - int getAfferentCoupling() { result = count(ModuleMetricsWithPointsTo t | t.dependsOn(this)) } + override int getAfferentCoupling() { + result = count(ModuleMetricsWithPointsTo t | t.dependsOn(this)) + } /** * Gets the efferent coupling of a module -- the number of modules that * it directly depends on. */ - int getEfferentCoupling() { result = count(ModuleMetricsWithPointsTo t | this.dependsOn(t)) } + override int getEfferentCoupling() { + result = count(ModuleMetricsWithPointsTo t | this.dependsOn(t)) + } private predicate dependsOn(Module other) { other != this and diff --git a/python/ql/lib/semmle/python/Metrics.qll b/python/ql/lib/semmle/python/Metrics.qll index fcbfdbe58b3..e00acca4587 100644 --- a/python/ql/lib/semmle/python/Metrics.qll +++ b/python/ql/lib/semmle/python/Metrics.qll @@ -54,6 +54,31 @@ class FunctionMetrics extends Function { result = e - n + 2 ) } + + /** + * Gets a function that this function depends on via a call. + * One callable depends on another if it makes a call that resolves to that callable. + */ + FunctionMetrics getADependency() { + result != this and + not non_coupling_method(result) and + exists(CallNode call | + call.getScope() = this and + resolveCall(call, result, _) + ) + } + + /** + * Gets the afferent coupling of this function -- the number of + * functions that depend on it. + */ + int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } + + /** + * Gets the efferent coupling of this function -- the number of + * functions that it depends on. + */ + int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } } /** The metrics for a class */ @@ -170,6 +195,25 @@ class ClassMetrics extends Class { /** return Hitz and Montazeri Lack of Cohesion */ int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } + + private predicate dependsOn(Class other) { + other != this and + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getScope() = this and f2.getScope() = other + ) + } + + /** + * Gets the afferent coupling of this class -- the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } + + /** + * Gets the efferent coupling of this class -- the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } } class ModuleMetrics extends Module { @@ -184,6 +228,25 @@ class ModuleMetrics extends Module { /** Gets the number of lines of docstrings in the module */ int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + + private predicate dependsOn(Module other) { + other != this and + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getEnclosingModule() = this and f2.getEnclosingModule() = other + ) + } + + /** + * Gets the afferent coupling of this module -- the number of modules that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } + + /** + * Gets the efferent coupling of this module -- the number of modules that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } } predicate non_coupling_method(Function f) { diff --git a/python/ql/src/Metrics/ClassAfferentCoupling.ql b/python/ql/src/Metrics/ClassAfferentCoupling.ql index 69983f564ec..295e8c61a6c 100644 --- a/python/ql/src/Metrics/ClassAfferentCoupling.ql +++ b/python/ql/src/Metrics/ClassAfferentCoupling.ql @@ -11,7 +11,6 @@ */ import python -private import LegacyPointsTo -from ClassMetricsWithPointsTo cls +from ClassMetrics cls select cls, cls.getAfferentCoupling() as n order by n desc diff --git a/python/ql/src/Metrics/ClassEfferentCoupling.ql b/python/ql/src/Metrics/ClassEfferentCoupling.ql index 08016a83889..d960c0142e3 100644 --- a/python/ql/src/Metrics/ClassEfferentCoupling.ql +++ b/python/ql/src/Metrics/ClassEfferentCoupling.ql @@ -11,7 +11,6 @@ */ import python -private import LegacyPointsTo -from ClassMetricsWithPointsTo cls +from ClassMetrics cls select cls, cls.getEfferentCoupling() as n order by n desc diff --git a/python/ql/src/Metrics/ModuleAfferentCoupling.ql b/python/ql/src/Metrics/ModuleAfferentCoupling.ql index 7203d12ae84..7bf51433785 100644 --- a/python/ql/src/Metrics/ModuleAfferentCoupling.ql +++ b/python/ql/src/Metrics/ModuleAfferentCoupling.ql @@ -11,7 +11,6 @@ */ import python -private import LegacyPointsTo -from ModuleMetricsWithPointsTo m +from ModuleMetrics m select m, m.getAfferentCoupling() as n order by n desc diff --git a/python/ql/src/Metrics/ModuleEfferentCoupling.ql b/python/ql/src/Metrics/ModuleEfferentCoupling.ql index a392a44e751..51fdcf5423b 100644 --- a/python/ql/src/Metrics/ModuleEfferentCoupling.ql +++ b/python/ql/src/Metrics/ModuleEfferentCoupling.ql @@ -11,7 +11,6 @@ */ import python -private import LegacyPointsTo -from ModuleMetricsWithPointsTo m +from ModuleMetrics m select m, m.getEfferentCoupling() as n order by n desc