From ac1f3dc7e9ab5b8e599b4f2a51ab88c1caaeca2e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 29 Dec 2019 17:02:13 -0500 Subject: [PATCH 1/2] Add 'parent' attributes to vars/methods/classes - The parent attribute already exists, but sometimes it was a string and sometimes it was a thing. Now it's always a thing --- CppHeaderParser/CppHeaderParser.py | 50 +++++++++++++++++------------- test/test_CppHeaderParser.py | 6 ++-- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 4978d47..8b1f275 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -589,6 +589,7 @@ class CppClass(dict): * ``nested_classes`` - Classes and structs defined within this class * ``final`` - True if final * ``abstract`` - True if abstract + * ``parent`` - If not None, the class that this class is nested in An example of how this could look is as follows:: @@ -713,7 +714,7 @@ def show(self): if "doxygen" in list(self.keys()): rtn += self["doxygen"] + "\n" if "parent" in list(self.keys()) and self["parent"]: - rtn += "parent class: " + self["parent"] + "\n" + rtn += "parent class: " + self["parent"]["name"] + "\n" if "inherits" in list(self.keys()): rtn += " Inherits: " @@ -759,7 +760,7 @@ def __str__(self): if "doxygen" in list(self.keys()): rtn += self["doxygen"] + "\n" if "parent" in list(self.keys()) and self["parent"]: - rtn += "parent class: " + self["parent"] + "\n" + rtn += "parent class: " + self["parent"]["name"] + "\n" if "inherits" in list(self.keys()) and len(self["inherits"]): rtn += "Inherits: " @@ -834,7 +835,7 @@ def __str__(self): if "doxygen" in list(self.keys()): rtn += self["doxygen"] + "\n" if "parent" in list(self.keys()) and self["parent"]: - rtn += "parent class: " + self["parent"] + "\n" + rtn += "parent class: " + self["parent"]["name"] + "\n" rtn += "{\n" for member in self["members"]: @@ -898,6 +899,7 @@ def _params_helper1(self, stack): def _params_helper2(self, params): for p in params: p["method"] = self # save reference in variable to parent method + p["parent"] = self if "::" in p["type"]: ns = p["type"].split("::")[0] if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES: @@ -914,6 +916,7 @@ class CppMethod(_CppMethod): * ``name`` - Name of the method * ``doxygen`` - Doxygen comments associated with the method if they exist * ``parameters`` - List of :class:`.CppVariable` + * ``parent`` - If not None, the class this method belongs to """ def show(self): @@ -1538,14 +1541,16 @@ def finalize_vars(self): nestedEnum = None nestedStruct = None nestedTypedef = None - if "method" in var and "parent" in list(var["method"].keys()): - klass = var["method"]["parent"] - if tag in var["method"]["parent"]._public_enums: - nestedEnum = var["method"]["parent"]._public_enums[tag] - elif tag in var["method"]["parent"]._public_typedefs: - nestedTypedef = var["method"]["parent"]._public_typedefs[ - tag - ] + + parent = var["parent"] + while parent: + nestedEnum = getattr(parent, "_public_enums", {}).get(tag) + if nestedEnum: + break + nestedTypedef = getattr(parent, "_public_typedefs", {}).get(tag) + if nestedTypedef: + break + parent = parent["parent"] if "<" in tag: # should also contain '>' var["template"] = tag # do not resolve templates @@ -1604,7 +1609,7 @@ def finalize_vars(self): var["enum"] = enum["namespace"] + enum["name"] var["fundamental"] = True - elif var["parent"]: + elif var["parent"] and var["unresolved"]: warning_print("WARN unresolved %s", _tag) var["ctypes_type"] = "ctypes.c_void_p" var["unresolved"] = True @@ -1750,8 +1755,8 @@ def finalize_vars(self): var["raw_type"] = ( var["class"]["namespace"] + "::" + var["raw_type"] ) - elif var["class"]["parent"] in self.classes: - parent = self.classes[var["class"]["parent"]] + else: + parent = var["class"]["parent"] var["raw_type"] = ( parent["namespace"] + "::" @@ -1759,8 +1764,6 @@ def finalize_vars(self): + "::" + var["raw_type"] ) - else: - var["unresolved"] = True elif ( "::" in var["raw_type"] @@ -2166,6 +2169,7 @@ def _evaluate_method_stack(self): self._get_stmt_doxygen(), self._get_location(self.nameStack), ) + newMethod["parent"] = None self.functions.append(newMethod) global parseHistory parseHistory.append( @@ -2299,6 +2303,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): klass = self.classes[self.curClass] klass["properties"][self.curAccessSpecifier].append(newVar) newVar["property_of_class"] = klass["name"] + newVar["parent"] = klass parseHistory.append( {"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar} ) @@ -2373,16 +2378,17 @@ def _evaluate_class_stack(self): if parent: newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent - newClass["parent"] = parent + newClass["parent"] = self.classes[parent] self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## self.curClass = key = parent + "::" + classKey self._classes_brace_level[key] = self.braceDepth elif newClass["parent"]: # nested class defined outside of parent. A::B {...} - parent = newClass["parent"] - newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent - self.classes[parent]["nested_classes"].append(newClass) + pcls = newClass["parent"] + parent = pcls["name"] + newClass["namespace"] = pcls["namespace"] + "::" + parent + pcls["nested_classes"].append(newClass) ## supports nested classes with the same name ## self.curClass = key = parent + "::" + classKey self._classes_brace_level[key] = self.braceDepth @@ -2767,7 +2773,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curAccessSpecifier = self.accessSpecifierStack[-1] self.accessSpecifierStack = self.accessSpecifierStack[:-1] if self.curClass and self.classes[self.curClass]["parent"]: - self.curClass = self.classes[self.curClass]["parent"] + self.curClass = self.classes[self.curClass]["parent"][ + "name" + ] else: self.curClass = "" self.stack = [] diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 2b78eff..2729656 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -952,7 +952,7 @@ def test_property_CONST_A(self): "unresolved": False, "constant": 1, "name": "CONST_A", - "parent": None, + "parent": self.cppHeader.classes["PandaClass"], "pointer": 0, "namespace": "", "raw_type": "int", @@ -981,7 +981,7 @@ def test_property_CONST_B(self): "unresolved": False, "constant": 1, "name": "CONST_B", - "parent": None, + "parent": self.cppHeader.classes["PandaClass"], "pointer": 0, "namespace": "", "raw_type": "int", @@ -1052,7 +1052,7 @@ def test_property(self): def test_union(self): cmp_values = { "name": "union HogUnion", - "parent": "HogClass", + "parent": self.cppHeader.classes["HogClass"], "declaration_method": "union", } self.assertEqual( From a1f5eae8f1d9668521200657cfcf2ade7eafd08c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 29 Dec 2019 17:09:20 -0500 Subject: [PATCH 2/2] using directives in a class are slightly different from global using --- CppHeaderParser/CppHeaderParser.py | 74 ++++++++++++++++++++++-------- test/test_CppHeaderParser.py | 47 ++++++++++++++++++- 2 files changed, 100 insertions(+), 21 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 8b1f275..1a6b0ca 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -589,6 +589,8 @@ class CppClass(dict): * ``nested_classes`` - Classes and structs defined within this class * ``final`` - True if final * ``abstract`` - True if abstract + * ``using`` - Using directives in this class scope: key is name for lookup, + value is :class:`.CppVariable` * ``parent`` - If not None, the class that this class is nested in An example of how this could look is as follows:: @@ -654,6 +656,7 @@ def __init__(self, nameStack, curTemplate, doxygen, location): self._public_typedefs = {} self._public_forward_declares = [] self["namespace"] = "" + self["using"] = {} debug_print("Class: %s", nameStack) debug_print("Template: %s", curTemplate) @@ -1124,12 +1127,14 @@ class CppVariable(_CppVariable): * ``default`` - Default value of the variable, this key will only exist if there is a default value * ``extern`` - True if its an extern, False if not + * ``parent`` - If not None, either the class this is a property of, or the + method this variable is a parameter in """ Vars = [] def __init__(self, nameStack, doxygen, location, **kwargs): - debug_print("trace %s", nameStack) + debug_print("var trace %s", nameStack) if len(nameStack) and nameStack[0] == "extern": self["extern"] = True del nameStack[0] @@ -1497,21 +1502,33 @@ def resolve_type(self, string, result): # recursive result["fundamental"] = False result["class"] = klass result["unresolved"] = False - elif self.using: - # search for type in all enclosing namespaces - for ns in _iter_ns_str_reversed(result.get("namespace", "")): - nsalias = ns + alias - used = self.using.get(nsalias) - if used: - for i in ("type", "namespace", "ctypes_type", "raw_type"): - if i in used: - result[i] = used[i] - result["unresolved"] = False - break - else: - result["unresolved"] = True else: - result["unresolved"] = True + used = None + + # Search for using directives in parents + parent = result["parent"] + while parent: + p_using = parent.get("using") + if p_using: + used = p_using.get(alias) + if used: + break + parent = parent["parent"] + + if not used and self.using: + # search for type in all enclosing namespaces + # TODO: would be nice if namespaces were an object? + for ns in _iter_ns_str_reversed(result.get("namespace", "")): + nsalias = ns + alias + used = self.using.get(nsalias) + if used: + break + + if used: + for i in ("type", "namespace", "ctypes_type", "raw_type"): + if i in used: + result[i] = used[i] + result["unresolved"] = False else: result["fundamental"] = True result["unresolved"] = False @@ -2544,8 +2561,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.anon_struct_counter = 0 self.anon_union_counter = [-1, 0] - #: Using directives in this header: key is full name for lookup, value - #: is :class:`.CppVariable` + #: Using directives in this header outside of class scope: key is + #: full name for lookup, value is :class:`.CppVariable` self.using = {} if len(self.headerFileName): @@ -3116,23 +3133,40 @@ def _evaluate_stack(self, token=None): else: if len(self.nameStack) > 3 and self.nameStack[2] == "=": # using foo = ns::bar + # -> type alias: same behavior in all scopes alias = self.nameStack[1] ns, stack = _split_namespace(self.nameStack[3:]) atype = CppVariable( stack, self._get_stmt_doxygen(), self._get_location(stack) ) + + # namespace refers to the embedded type + atype["namespace"] = ns else: # using foo::bar + # -> in global scope this is bringing in something + # from a different namespace + # -> in class scope this is bringing in a member + # from a base class ns, stack = _split_namespace(self.nameStack[1:]) atype = CppVariable( stack, self._get_stmt_doxygen(), self._get_location(stack) ) alias = atype["type"] + if self.curClass: + atype["baseclass"] = ns + else: + atype["namespace"] = ns - atype["namespace"] = ns atype["raw_type"] = ns + atype["type"] - alias = self.current_namespace() + alias - self.using[alias] = atype + + if self.curClass: + klass = self.classes[self.curClass] + klass["using"][alias] = atype + else: + # lookup is done + alias = self.current_namespace() + alias + self.using[alias] = atype elif is_method_namestack(self.stack) and "(" in self.nameStack: debug_print("trace") self._evaluate_method_stack() diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 2729656..1f35355 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2787,18 +2787,26 @@ def setUp(self): using VoidFunction = std::function; void fn(string &s, VoidFunction fn, thing * t); + + class A : public B { + public: + using B::B; + using IntFunction = std::function; + + void a(string &s, IntFunction fn, thing * t); + }; } """, "string", ) def test_using(self): + self.assertEqual(len(self.cppHeader.using), 3) self.assertIn("a::string", self.cppHeader.using) self.assertIn("a::VoidFunction", self.cppHeader.using) self.assertIn("thing", self.cppHeader.using) def test_fn(self): - self.maxDiff = None self.assertEqual(len(self.cppHeader.functions), 1) fn = self.cppHeader.functions[0] self.assertEqual(fn["name"], "fn") @@ -2829,6 +2837,43 @@ def test_fn(self): ], ) + def test_class(self): + c = self.cppHeader.classes["A"] + + self.assertEqual(len(c["using"]), 2) + self.assertIn("B", c["using"]) + self.assertIn("IntFunction", c["using"]) + + self.assertEqual(len(c["methods"]["public"]), 1) + fn = c["methods"]["public"][0] + self.assertEqual(fn["name"], "a") + self.assertEqual( + filter_pameters(fn["parameters"], ["namespace", "raw_type"]), + [ + { + "type": "string", + "name": "s", + "desc": None, + "namespace": "std::", + "raw_type": "std::string", + }, + { + "type": "function", + "name": "fn", + "desc": None, + "namespace": "std::", + "raw_type": "std::function", + }, + { + "type": "thing", + "name": "t", + "desc": None, + "namespace": "std::", + "raw_type": "std::thing", + }, + ], + ) + class StaticFn_TestCase(unittest.TestCase): def setUp(self): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy