Skip to content

Commit 1811a2a

Browse files
authored
Merge pull request robotpy#33 from robotpy/fix-using-class
Fix using class
2 parents 08950fc + a1f5eae commit 1811a2a

File tree

2 files changed

+132
-45
lines changed

2 files changed

+132
-45
lines changed

CppHeaderParser/CppHeaderParser.py

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,9 @@ class CppClass(dict):
589589
* ``nested_classes`` - Classes and structs defined within this class
590590
* ``final`` - True if final
591591
* ``abstract`` - True if abstract
592+
* ``using`` - Using directives in this class scope: key is name for lookup,
593+
value is :class:`.CppVariable`
594+
* ``parent`` - If not None, the class that this class is nested in
592595
593596
An example of how this could look is as follows::
594597
@@ -653,6 +656,7 @@ def __init__(self, nameStack, curTemplate, doxygen, location):
653656
self._public_typedefs = {}
654657
self._public_forward_declares = []
655658
self["namespace"] = ""
659+
self["using"] = {}
656660

657661
debug_print("Class: %s", nameStack)
658662
debug_print("Template: %s", curTemplate)
@@ -713,7 +717,7 @@ def show(self):
713717
if "doxygen" in list(self.keys()):
714718
rtn += self["doxygen"] + "\n"
715719
if "parent" in list(self.keys()) and self["parent"]:
716-
rtn += "parent class: " + self["parent"] + "\n"
720+
rtn += "parent class: " + self["parent"]["name"] + "\n"
717721

718722
if "inherits" in list(self.keys()):
719723
rtn += " Inherits: "
@@ -759,7 +763,7 @@ def __str__(self):
759763
if "doxygen" in list(self.keys()):
760764
rtn += self["doxygen"] + "\n"
761765
if "parent" in list(self.keys()) and self["parent"]:
762-
rtn += "parent class: " + self["parent"] + "\n"
766+
rtn += "parent class: " + self["parent"]["name"] + "\n"
763767

764768
if "inherits" in list(self.keys()) and len(self["inherits"]):
765769
rtn += "Inherits: "
@@ -834,7 +838,7 @@ def __str__(self):
834838
if "doxygen" in list(self.keys()):
835839
rtn += self["doxygen"] + "\n"
836840
if "parent" in list(self.keys()) and self["parent"]:
837-
rtn += "parent class: " + self["parent"] + "\n"
841+
rtn += "parent class: " + self["parent"]["name"] + "\n"
838842

839843
rtn += "{\n"
840844
for member in self["members"]:
@@ -898,6 +902,7 @@ def _params_helper1(self, stack):
898902
def _params_helper2(self, params):
899903
for p in params:
900904
p["method"] = self # save reference in variable to parent method
905+
p["parent"] = self
901906
if "::" in p["type"]:
902907
ns = p["type"].split("::")[0]
903908
if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES:
@@ -914,6 +919,7 @@ class CppMethod(_CppMethod):
914919
* ``name`` - Name of the method
915920
* ``doxygen`` - Doxygen comments associated with the method if they exist
916921
* ``parameters`` - List of :class:`.CppVariable`
922+
* ``parent`` - If not None, the class this method belongs to
917923
"""
918924

919925
def show(self):
@@ -1121,12 +1127,14 @@ class CppVariable(_CppVariable):
11211127
* ``default`` - Default value of the variable, this key will only
11221128
exist if there is a default value
11231129
* ``extern`` - True if its an extern, False if not
1130+
* ``parent`` - If not None, either the class this is a property of, or the
1131+
method this variable is a parameter in
11241132
"""
11251133

11261134
Vars = []
11271135

11281136
def __init__(self, nameStack, doxygen, location, **kwargs):
1129-
debug_print("trace %s", nameStack)
1137+
debug_print("var trace %s", nameStack)
11301138
if len(nameStack) and nameStack[0] == "extern":
11311139
self["extern"] = True
11321140
del nameStack[0]
@@ -1494,21 +1502,33 @@ def resolve_type(self, string, result): # recursive
14941502
result["fundamental"] = False
14951503
result["class"] = klass
14961504
result["unresolved"] = False
1497-
elif self.using:
1498-
# search for type in all enclosing namespaces
1499-
for ns in _iter_ns_str_reversed(result.get("namespace", "")):
1500-
nsalias = ns + alias
1501-
used = self.using.get(nsalias)
1502-
if used:
1503-
for i in ("type", "namespace", "ctypes_type", "raw_type"):
1504-
if i in used:
1505-
result[i] = used[i]
1506-
result["unresolved"] = False
1507-
break
1508-
else:
1509-
result["unresolved"] = True
15101505
else:
1511-
result["unresolved"] = True
1506+
used = None
1507+
1508+
# Search for using directives in parents
1509+
parent = result["parent"]
1510+
while parent:
1511+
p_using = parent.get("using")
1512+
if p_using:
1513+
used = p_using.get(alias)
1514+
if used:
1515+
break
1516+
parent = parent["parent"]
1517+
1518+
if not used and self.using:
1519+
# search for type in all enclosing namespaces
1520+
# TODO: would be nice if namespaces were an object?
1521+
for ns in _iter_ns_str_reversed(result.get("namespace", "")):
1522+
nsalias = ns + alias
1523+
used = self.using.get(nsalias)
1524+
if used:
1525+
break
1526+
1527+
if used:
1528+
for i in ("type", "namespace", "ctypes_type", "raw_type"):
1529+
if i in used:
1530+
result[i] = used[i]
1531+
result["unresolved"] = False
15121532
else:
15131533
result["fundamental"] = True
15141534
result["unresolved"] = False
@@ -1538,14 +1558,16 @@ def finalize_vars(self):
15381558
nestedEnum = None
15391559
nestedStruct = None
15401560
nestedTypedef = None
1541-
if "method" in var and "parent" in list(var["method"].keys()):
1542-
klass = var["method"]["parent"]
1543-
if tag in var["method"]["parent"]._public_enums:
1544-
nestedEnum = var["method"]["parent"]._public_enums[tag]
1545-
elif tag in var["method"]["parent"]._public_typedefs:
1546-
nestedTypedef = var["method"]["parent"]._public_typedefs[
1547-
tag
1548-
]
1561+
1562+
parent = var["parent"]
1563+
while parent:
1564+
nestedEnum = getattr(parent, "_public_enums", {}).get(tag)
1565+
if nestedEnum:
1566+
break
1567+
nestedTypedef = getattr(parent, "_public_typedefs", {}).get(tag)
1568+
if nestedTypedef:
1569+
break
1570+
parent = parent["parent"]
15491571

15501572
if "<" in tag: # should also contain '>'
15511573
var["template"] = tag # do not resolve templates
@@ -1604,7 +1626,7 @@ def finalize_vars(self):
16041626
var["enum"] = enum["namespace"] + enum["name"]
16051627
var["fundamental"] = True
16061628

1607-
elif var["parent"]:
1629+
elif var["parent"] and var["unresolved"]:
16081630
warning_print("WARN unresolved %s", _tag)
16091631
var["ctypes_type"] = "ctypes.c_void_p"
16101632
var["unresolved"] = True
@@ -1750,17 +1772,15 @@ def finalize_vars(self):
17501772
var["raw_type"] = (
17511773
var["class"]["namespace"] + "::" + var["raw_type"]
17521774
)
1753-
elif var["class"]["parent"] in self.classes:
1754-
parent = self.classes[var["class"]["parent"]]
1775+
else:
1776+
parent = var["class"]["parent"]
17551777
var["raw_type"] = (
17561778
parent["namespace"]
17571779
+ "::"
17581780
+ var["class"]["name"]
17591781
+ "::"
17601782
+ var["raw_type"]
17611783
)
1762-
else:
1763-
var["unresolved"] = True
17641784

17651785
elif (
17661786
"::" in var["raw_type"]
@@ -2166,6 +2186,7 @@ def _evaluate_method_stack(self):
21662186
self._get_stmt_doxygen(),
21672187
self._get_location(self.nameStack),
21682188
)
2189+
newMethod["parent"] = None
21692190
self.functions.append(newMethod)
21702191
global parseHistory
21712192
parseHistory.append(
@@ -2299,6 +2320,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None):
22992320
klass = self.classes[self.curClass]
23002321
klass["properties"][self.curAccessSpecifier].append(newVar)
23012322
newVar["property_of_class"] = klass["name"]
2323+
newVar["parent"] = klass
23022324
parseHistory.append(
23032325
{"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar}
23042326
)
@@ -2373,16 +2395,17 @@ def _evaluate_class_stack(self):
23732395

23742396
if parent:
23752397
newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent
2376-
newClass["parent"] = parent
2398+
newClass["parent"] = self.classes[parent]
23772399
self.classes[parent]["nested_classes"].append(newClass)
23782400
## supports nested classes with the same name ##
23792401
self.curClass = key = parent + "::" + classKey
23802402
self._classes_brace_level[key] = self.braceDepth
23812403

23822404
elif newClass["parent"]: # nested class defined outside of parent. A::B {...}
2383-
parent = newClass["parent"]
2384-
newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent
2385-
self.classes[parent]["nested_classes"].append(newClass)
2405+
pcls = newClass["parent"]
2406+
parent = pcls["name"]
2407+
newClass["namespace"] = pcls["namespace"] + "::" + parent
2408+
pcls["nested_classes"].append(newClass)
23862409
## supports nested classes with the same name ##
23872410
self.curClass = key = parent + "::" + classKey
23882411
self._classes_brace_level[key] = self.braceDepth
@@ -2538,8 +2561,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
25382561
self.anon_struct_counter = 0
25392562
self.anon_union_counter = [-1, 0]
25402563

2541-
#: Using directives in this header: key is full name for lookup, value
2542-
#: is :class:`.CppVariable`
2564+
#: Using directives in this header outside of class scope: key is
2565+
#: full name for lookup, value is :class:`.CppVariable`
25432566
self.using = {}
25442567

25452568
if len(self.headerFileName):
@@ -2767,7 +2790,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
27672790
self.curAccessSpecifier = self.accessSpecifierStack[-1]
27682791
self.accessSpecifierStack = self.accessSpecifierStack[:-1]
27692792
if self.curClass and self.classes[self.curClass]["parent"]:
2770-
self.curClass = self.classes[self.curClass]["parent"]
2793+
self.curClass = self.classes[self.curClass]["parent"][
2794+
"name"
2795+
]
27712796
else:
27722797
self.curClass = ""
27732798
self.stack = []
@@ -3108,23 +3133,40 @@ def _evaluate_stack(self, token=None):
31083133
else:
31093134
if len(self.nameStack) > 3 and self.nameStack[2] == "=":
31103135
# using foo = ns::bar
3136+
# -> type alias: same behavior in all scopes
31113137
alias = self.nameStack[1]
31123138
ns, stack = _split_namespace(self.nameStack[3:])
31133139
atype = CppVariable(
31143140
stack, self._get_stmt_doxygen(), self._get_location(stack)
31153141
)
3142+
3143+
# namespace refers to the embedded type
3144+
atype["namespace"] = ns
31163145
else:
31173146
# using foo::bar
3147+
# -> in global scope this is bringing in something
3148+
# from a different namespace
3149+
# -> in class scope this is bringing in a member
3150+
# from a base class
31183151
ns, stack = _split_namespace(self.nameStack[1:])
31193152
atype = CppVariable(
31203153
stack, self._get_stmt_doxygen(), self._get_location(stack)
31213154
)
31223155
alias = atype["type"]
3156+
if self.curClass:
3157+
atype["baseclass"] = ns
3158+
else:
3159+
atype["namespace"] = ns
31233160

3124-
atype["namespace"] = ns
31253161
atype["raw_type"] = ns + atype["type"]
3126-
alias = self.current_namespace() + alias
3127-
self.using[alias] = atype
3162+
3163+
if self.curClass:
3164+
klass = self.classes[self.curClass]
3165+
klass["using"][alias] = atype
3166+
else:
3167+
# lookup is done
3168+
alias = self.current_namespace() + alias
3169+
self.using[alias] = atype
31283170
elif is_method_namestack(self.stack) and "(" in self.nameStack:
31293171
debug_print("trace")
31303172
self._evaluate_method_stack()

test/test_CppHeaderParser.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ def test_property_CONST_A(self):
952952
"unresolved": False,
953953
"constant": 1,
954954
"name": "CONST_A",
955-
"parent": None,
955+
"parent": self.cppHeader.classes["PandaClass"],
956956
"pointer": 0,
957957
"namespace": "",
958958
"raw_type": "int",
@@ -981,7 +981,7 @@ def test_property_CONST_B(self):
981981
"unresolved": False,
982982
"constant": 1,
983983
"name": "CONST_B",
984-
"parent": None,
984+
"parent": self.cppHeader.classes["PandaClass"],
985985
"pointer": 0,
986986
"namespace": "",
987987
"raw_type": "int",
@@ -1052,7 +1052,7 @@ def test_property(self):
10521052
def test_union(self):
10531053
cmp_values = {
10541054
"name": "union HogUnion",
1055-
"parent": "HogClass",
1055+
"parent": self.cppHeader.classes["HogClass"],
10561056
"declaration_method": "union",
10571057
}
10581058
self.assertEqual(
@@ -2787,18 +2787,26 @@ def setUp(self):
27872787
using VoidFunction = std::function<void()>;
27882788
27892789
void fn(string &s, VoidFunction fn, thing * t);
2790+
2791+
class A : public B {
2792+
public:
2793+
using B::B;
2794+
using IntFunction = std::function<int()>;
2795+
2796+
void a(string &s, IntFunction fn, thing * t);
2797+
};
27902798
}
27912799
""",
27922800
"string",
27932801
)
27942802

27952803
def test_using(self):
2804+
self.assertEqual(len(self.cppHeader.using), 3)
27962805
self.assertIn("a::string", self.cppHeader.using)
27972806
self.assertIn("a::VoidFunction", self.cppHeader.using)
27982807
self.assertIn("thing", self.cppHeader.using)
27992808

28002809
def test_fn(self):
2801-
self.maxDiff = None
28022810
self.assertEqual(len(self.cppHeader.functions), 1)
28032811
fn = self.cppHeader.functions[0]
28042812
self.assertEqual(fn["name"], "fn")
@@ -2829,6 +2837,43 @@ def test_fn(self):
28292837
],
28302838
)
28312839

2840+
def test_class(self):
2841+
c = self.cppHeader.classes["A"]
2842+
2843+
self.assertEqual(len(c["using"]), 2)
2844+
self.assertIn("B", c["using"])
2845+
self.assertIn("IntFunction", c["using"])
2846+
2847+
self.assertEqual(len(c["methods"]["public"]), 1)
2848+
fn = c["methods"]["public"][0]
2849+
self.assertEqual(fn["name"], "a")
2850+
self.assertEqual(
2851+
filter_pameters(fn["parameters"], ["namespace", "raw_type"]),
2852+
[
2853+
{
2854+
"type": "string",
2855+
"name": "s",
2856+
"desc": None,
2857+
"namespace": "std::",
2858+
"raw_type": "std::string",
2859+
},
2860+
{
2861+
"type": "function<int ( )>",
2862+
"name": "fn",
2863+
"desc": None,
2864+
"namespace": "std::",
2865+
"raw_type": "std::function<int ( )>",
2866+
},
2867+
{
2868+
"type": "thing",
2869+
"name": "t",
2870+
"desc": None,
2871+
"namespace": "std::",
2872+
"raw_type": "std::thing",
2873+
},
2874+
],
2875+
)
2876+
28322877

28332878
class StaticFn_TestCase(unittest.TestCase):
28342879
def setUp(self):

0 commit comments

Comments
 (0)
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