From 8c9204a3452df3dd895d54d4ebe032d419a126f9 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 20 Oct 2023 10:55:23 +0200 Subject: [PATCH] Python: Explain the funky logic in Find.ql --- python/ql/src/meta/ClassHierarchy/Find.ql | 13 +++++++++++++ .../library-tests/FindSubclass/Find.expected | 1 + .../FindSubclass/find_subclass_test.py | 3 +++ 3 files changed, 17 insertions(+) diff --git a/python/ql/src/meta/ClassHierarchy/Find.ql b/python/ql/src/meta/ClassHierarchy/Find.ql index 264605785cf..f20fb185d29 100644 --- a/python/ql/src/meta/ClassHierarchy/Find.ql +++ b/python/ql/src/meta/ClassHierarchy/Find.ql @@ -478,6 +478,19 @@ predicate fullyQualifiedToYamlFormat(string fullyQualified, string type2, string from FindSubclassesSpec spec, string newModelFullyQualified, string type2, string path, Module mod where newModel(spec, newModelFullyQualified, _, mod, _) and + // Since a class C which is a subclass for flask.MethodView is always a subclass of + // flask.View, and we chose to care about this distinction, in a naive approach we + // would always record rows for _both_ specs... that's just wasteful, so instead we + // only record the row for the more specific spec -- this is captured by the + // .getSuperClass() method on a spec, which can links specs together in this way. + // However, if the definition actually depends on some logic, like below, we should + // still record both rows + // ``` + // if : + // class C(flask.View): ... + // else: + // class C(flask.MethodView): ... + // ``` not exists(FindSubclassesSpec subclass | subclass.getSuperClass() = spec | newModel(subclass, newModelFullyQualified, _, mod, _) ) and diff --git a/python/ql/test/experimental/library-tests/FindSubclass/Find.expected b/python/ql/test/experimental/library-tests/FindSubclass/Find.expected index d30384a0aef..1d12a2e2d9d 100644 --- a/python/ql/test/experimental/library-tests/FindSubclass/Find.expected +++ b/python/ql/test/experimental/library-tests/FindSubclass/Find.expected @@ -1,3 +1,4 @@ +| flask.MethodView~Subclass | find_subclass_test | Member[C] | | flask.MethodView~Subclass | find_subclass_test | Member[MethodView] | | flask.MethodView~Subclass | find_subclass_test | Member[clash] | | flask.View~Subclass | find_subclass_test | Member[A] | diff --git a/python/ql/test/experimental/library-tests/FindSubclass/find_subclass_test.py b/python/ql/test/experimental/library-tests/FindSubclass/find_subclass_test.py index afcd1806c94..d0f0048338f 100644 --- a/python/ql/test/experimental/library-tests/FindSubclass/find_subclass_test.py +++ b/python/ql/test/experimental/library-tests/FindSubclass/find_subclass_test.py @@ -1,4 +1,5 @@ from flask.views import View +import flask.views class A(View): pass @@ -6,6 +7,8 @@ class A(View): class B(A): pass +class C(flask.views.MethodView): + pass ViewAlias = View