diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index f142278..3beb3d0 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -936,7 +936,17 @@ def _params_helper1(self, stack): elif a.startswith("__attribute__((__const__))"): stack = stack[6:] - stack = stack[stack.index("(") + 1 :] + last_paren_index = len(stack) - stack[-1::-1].index(")") - 1 + open_paren_count = 1 + method_paren_start_idx = last_paren_index-1 + while open_paren_count > 0: + if stack[method_paren_start_idx] == ")": + open_paren_count+=1 + elif stack[method_paren_start_idx] == "(": + open_paren_count-=1 + method_paren_start_idx -= 1 + + stack = stack[method_paren_start_idx + 2 :] if not stack: return [] if ( @@ -998,33 +1008,6 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location if doxygen: self["doxygen"] = doxygen - # Remove leading keywords - for i, word in enumerate(nameStack): - if word not in Resolver.C_KEYWORDS: - nameStack = nameStack[i:] - break - - if "operator" in nameStack: - rtnType = " ".join(nameStack[: nameStack.index("operator")]) - self["name"] = "".join( - nameStack[nameStack.index("operator") : nameStack.index("(")] - ) - else: - rtnType = " ".join(nameStack[: nameStack.index("(") - 1]) - self["name"] = " ".join( - nameStack[nameStack.index("(") - 1 : nameStack.index("(")] - ) - - if len(rtnType) == 0 or self["name"] == curClass: - rtnType = "void" - - self["rtnType"] = ( - rtnType.replace(" :: ", "::") - .replace(" < ", "<") - .replace(" > ", "> ") - .replace(">>", "> >") - .replace(" ,", ",") - ) # deal with "noexcept" specifier/operator self["noexcept"] = None @@ -1064,6 +1047,8 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location break self.update(methinfo) + if len(self["rtnType"]) == 0 or self["name"] == curClass: + self["rtnType"] = "void" set_location_info(self, location) paramsStack = self._params_helper1(nameStack) @@ -1304,6 +1289,7 @@ def __init__(self, nameStack, doxygen, location, **kwargs): def _filter_name(self, name): name = name.replace(" :", ":").replace(": ", ":") name = name.replace(" < ", "<") + name = name.replace(" ( ", "(").replace(" ) ", ")") name = name.replace(" > ", "> ").replace(">>", "> >") name = name.replace(") >", ")>") name = name.replace(" {", "{").replace(" }", "}") @@ -2168,7 +2154,7 @@ def finalize(self): } def parse_method_type(self, stack): - trace_print("meth type info", stack) + debug_print("meth type info %s", stack) info = { "debug": " ".join(stack) .replace(" :: ", "::") @@ -2183,13 +2169,57 @@ def parse_method_type(self, stack): info.update(self._method_type_defaults) - header = stack[: stack.index("(")] + last_paren_index = len(stack) - stack[-1::-1].index(")") - 1 + open_paren_count = 1 + method_paren_start_idx = last_paren_index-1 + while open_paren_count > 0: + if stack[method_paren_start_idx] == ")": + open_paren_count+=1 + elif stack[method_paren_start_idx] == "(": + open_paren_count-=1 + method_paren_start_idx -= 1 + + header = stack[: method_paren_start_idx+1] header = " ".join(header) + # Replace fields that would mess up our re-split below header = header.replace(" :: ", "::") header = header.replace(" < ", "<") header = header.replace(" > ", "> ") + header = header.replace("> >", ">>") header = header.replace("default ", "default") header = header.strip() + # Remove leading keywords, splitting on spaces to avoid removing keywords embedded in other words + + # Re-split to find method declarations like A::B::meth() that were formed by joining separate tokens + header = header.split() + name = header.pop() + for word in Resolver.C_KEYWORDS.union(set(ignoreSymbols)): + if word in header: + info[word] = True + header.remove(word) + header = " ".join(header) + # Now replace fields for aesthetics + header = header.replace(" (", "(") + header = header.replace("( ", "(") + header = header.replace(" )", ")") + header = header.replace(") ", ")") + header = header.replace(" ,", ",") + if "operator" in stack: + info["rtnType"] = " ".join(stack[: stack.index("operator")]) + op = "".join( + stack[stack.index("operator")+1 : method_paren_start_idx+1] + ) + if not op: + if " ".join(["operator", "(", ")", "("]) in " ".join(stack): + op = "()" + else: + debug_print("Error parsing operator") + return None + name = "operator"+op + info["operator"] = op + else: + info["rtnType"] = header + info["returns"] = info["rtnType"] if stack[-1] == "{": info["defined"] = True @@ -2210,32 +2240,9 @@ def parse_method_type(self, stack): elif stack[-2] == "delete": info["deleted"] = True - r = header.split() - name = None - if "operator" in stack: # rare case op overload defined outside of class - op = stack[stack.index("operator") + 1 : stack.index("(")] - op = "".join(op) - if not op: - if " ".join(["operator", "(", ")", "("]) in " ".join(stack): - op = "()" - else: - trace_print("Error parsing operator") - return None - - info["operator"] = op - name = "operator" + op - a = stack[: stack.index("operator")] - - elif r: - name = r[-1] - a = r[:-1] # strip name - if name is None: - return None # if name.startswith('~'): name = name[1:] - while a and a[0] == "}": # strip - can have multiple } } - a = a[1:] if "::" in name: # klass,name = name.split('::') # methods can be defined outside of class @@ -2254,7 +2261,7 @@ def parse_method_type(self, stack): info["defined"] = True info["default"] = True name = name[1:] - elif not a or (name == self.curClass and len(self.curClass)): + elif (name == self.curClass and len(self.curClass)): info["constructor"] = True if "default;" in stack: info["defined"] = True @@ -2262,27 +2269,6 @@ def parse_method_type(self, stack): info["name"] = name - for tag in self.C_KEYWORDS: - if tag in a: - info[tag] = True - a.remove(tag) # inplace - if "template" in a: - a.remove("template") - b = " ".join(a) - if ">" in b: - info["template"] = b[: b.index(">") + 1] - info["returns"] = b[ - b.index(">") + 1 : - ] # find return type, could be incorrect... TODO - if "' - if typname not in self._template_typenames: - self._template_typenames.append(typname) - else: - info["returns"] = " ".join(a) - else: - info["returns"] = " ".join(a) info["returns"] = info["returns"].replace(" <", "<").strip() ## be careful with templates, do not count pointers inside template diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index c454a32..cfc35c7 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -1535,7 +1535,7 @@ def test_name_0(self): def test_type_0(self): self.assertEqual( self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0]["type"], - "void * ( * ) ( )", + "void *(* )()", ) def test_function_pointer_field_0(self): @@ -1555,7 +1555,7 @@ def test_name_1(self): def test_type_1(self): self.assertEqual( self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1]["type"], - "void ( * ) ( void * buf, int buflen )", + "void(* )(void * buf, int buflen )", ) def test_function_pointer_field_1(self): @@ -1631,7 +1631,7 @@ def setUp(self): def test_rtn_type(self): self.assertEqual( self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["rtnType"], - "std::map > >", + "std::map>>", ) def test_param_1_name(self): @@ -2538,7 +2538,7 @@ def test_set_callback(self): ) self.assertEqual( self.cppHeader.functions[8]["parameters"][1]["type"], - "long ( * ) ( struct test_st *, int, const char *, int long, long, long )", + "long(* )(struct test_st *, int, const char *, int long, long, long )", ) @@ -2851,8 +2851,8 @@ def test_using(self): "desc": None, "name": "", "namespace": "std::", - "raw_type": "std::function", - "type": "function", + "raw_type": "std::function", + "type": "function", "typealias": "VoidFunction", "using_type": "typealias", } @@ -2909,11 +2909,11 @@ def test_fn(self): "raw_type": "std::string", }, { - "type": "function", + "type": "function", "name": "fn", "desc": None, "namespace": "std::", - "raw_type": "std::function", + "raw_type": "std::function", }, { "type": "thing", @@ -2946,11 +2946,11 @@ def test_class(self): "raw_type": "std::string", }, { - "type": "function", + "type": "function", "name": "fn", "desc": None, "namespace": "std::", - "raw_type": "std::function", + "raw_type": "std::function", }, { "type": "thing", @@ -4063,5 +4063,73 @@ def test_fn(self): self.assertEqual(c.typedefs["mmmmp"], "typedef int ( * ) ( int , int )") +class DecltypeMethodReturnClass_meth1(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +#include +#include +using namespace std; + +class DecltypeMethodReturnClass +{ +public: + const int returnInt(); + + decltype(returnInt()) meth1(decltype(returnInt()) arg1); + + const std::optional meth2(const decltype(returnInt()) v1); + + template + decltype(T::Q) meth3(int v1); + +}; +""", + "string", + ) + + def test_name(self): + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][1]["name"], + "meth1", + ) + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][2]["name"], + "meth2", + ) + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][3]["name"], + "meth3", + ) + + def test_rtntype(self): + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][1]["rtnType"], + "decltype(returnInt())", + ) + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][2]["rtnType"], + "const std::optional", + ) + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][3]["rtnType"], + "decltype(T::Q)", + ) + + def test_parameters(self): + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][1]["parameters"][0]['type'], + "decltype(returnInt() )", + ) + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][2]["parameters"][0]['type'], + "const decltype(returnInt() )", + ) + self.assertEqual( + self.cppHeader.classes["DecltypeMethodReturnClass"]["methods"]["public"][3]["parameters"][0]['type'], + "int", + ) + + if __name__ == "__main__": unittest.main() 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