Skip to content

Fix using class #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 83 additions & 41 deletions CppHeaderParser/CppHeaderParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,9 @@ 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::

Expand Down Expand Up @@ -653,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)
Expand Down Expand Up @@ -713,7 +717,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: "
Expand Down Expand Up @@ -759,7 +763,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: "
Expand Down Expand Up @@ -834,7 +838,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"]:
Expand Down Expand Up @@ -898,6 +902,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:
Expand All @@ -914,6 +919,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):
Expand Down Expand Up @@ -1121,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]
Expand Down Expand Up @@ -1494,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
Expand Down Expand Up @@ -1538,14 +1558,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
Expand Down Expand Up @@ -1604,7 +1626,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
Expand Down Expand Up @@ -1750,17 +1772,15 @@ 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"]
+ "::"
+ var["class"]["name"]
+ "::"
+ var["raw_type"]
)
else:
var["unresolved"] = True

elif (
"::" in var["raw_type"]
Expand Down Expand Up @@ -2166,6 +2186,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(
Expand Down Expand Up @@ -2299,6 +2320,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}
)
Expand Down Expand Up @@ -2373,16 +2395,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
Expand Down Expand Up @@ -2538,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):
Expand Down Expand Up @@ -2767,7 +2790,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 = []
Expand Down Expand Up @@ -3108,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()
Expand Down
53 changes: 49 additions & 4 deletions test/test_CppHeaderParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -2787,18 +2787,26 @@ def setUp(self):
using VoidFunction = std::function<void()>;

void fn(string &s, VoidFunction fn, thing * t);

class A : public B {
public:
using B::B;
using IntFunction = std::function<int()>;

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")
Expand Down Expand Up @@ -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<int ( )>",
"name": "fn",
"desc": None,
"namespace": "std::",
"raw_type": "std::function<int ( )>",
},
{
"type": "thing",
"name": "t",
"desc": None,
"namespace": "std::",
"raw_type": "std::thing",
},
],
)


class StaticFn_TestCase(unittest.TestCase):
def setUp(self):
Expand Down
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