diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index 2930cc7..497eaee 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -359,6 +359,15 @@ class CxxParser: # Various # + _msvc_conventions = { + "__cdecl", + "__clrcall", + "__stdcall", + "__fastcall", + "__thiscall", + "__vectorcall", + } + def _parse_namespace( self, tok: LexToken, doxygen: typing.Optional[str], inline: bool = False ) -> None: @@ -1655,6 +1664,7 @@ class CxxParser: destructor: bool, is_friend: bool, is_typedef: bool, + msvc_convention: typing.Optional[LexToken], ) -> bool: """ Assumes the caller has already consumed the return type and name, this consumes the @@ -1670,6 +1680,8 @@ class CxxParser: raise self._parse_error(None) props = dict.fromkeys(mods.both.keys(), True) + if msvc_convention: + props["msvc_convention"] = msvc_convention.value state = self.state state.location = location @@ -1758,6 +1770,7 @@ class CxxParser: fn.vararg, fn.has_trailing_return, noexcept=fn.noexcept, + msvc_convention=fn.msvc_convention, ) typedef = Typedef(fntype, pqname.segments[0].name, self._current_access) @@ -1823,6 +1836,10 @@ class CxxParser: self._parse_trailing_return_type(dtype) else: + msvc_convention = self.lex.token_if_val(*self._msvc_conventions) + if msvc_convention: + msvc_convention = msvc_convention.value + # Check to see if this is a grouping paren or something else if not self.lex.token_peek_if("*", "&"): self.lex.return_token(tok) @@ -1840,7 +1857,9 @@ class CxxParser: fn_params, vararg = self._parse_parameters() # the type we already have is the return type of the function pointer - dtype = FunctionType(dtype, fn_params, vararg) + dtype = FunctionType( + dtype, fn_params, vararg, msvc_convention=msvc_convention + ) # the inner tokens must either be a * or a pqname that ends # with ::* (member function pointer) @@ -1975,6 +1994,7 @@ class CxxParser: constructor = False destructor = False op = None + msvc_convention = None # If we have a leading (, that's either an obnoxious grouping # paren or it's a constructor @@ -2021,6 +2041,8 @@ class CxxParser: self.lex.return_tokens(toks[1:-1]) if dtype: + msvc_convention = self.lex.token_if_val(*self._msvc_conventions) + tok = self.lex.token_if_in_set(self._pqname_start_tokens) if tok: pqname, op = self._parse_pqname(tok, fn_ok=True) @@ -2046,7 +2068,10 @@ class CxxParser: destructor, is_friend, is_typedef, + msvc_convention, ) + elif msvc_convention: + raise self._parse_error(msvc_convention) # anything else is a field/variable if is_friend: diff --git a/cxxheaderparser/types.py b/cxxheaderparser/types.py index becf6ef..f9a65cb 100644 --- a/cxxheaderparser/types.py +++ b/cxxheaderparser/types.py @@ -238,6 +238,14 @@ class FunctionType: noexcept: typing.Optional[Value] = None + #: Only set if an MSVC calling convention (__stdcall, etc) is explictly + #: specified. + #: + #: .. note:: If your code contains things like WINAPI, you will need to + #: use a preprocessor to transform it to the appropriate + #: calling convention + msvc_convention: typing.Optional[str] = None + @dataclass class Type: @@ -487,6 +495,14 @@ class Function: throw: typing.Optional[Value] = None noexcept: typing.Optional[Value] = None + #: Only set if an MSVC calling convention (__stdcall, etc) is explictly + #: specified. + #: + #: .. note:: If your code contains things like WINAPI, you will need to + #: use a preprocessor to transform it to the appropriate + #: calling convention + msvc_convention: typing.Optional[str] = None + @dataclass class Method(Function): diff --git a/tests/test_fn.py b/tests/test_fn.py index 651f922..c1daef0 100644 --- a/tests/test_fn.py +++ b/tests/test_fn.py @@ -20,6 +20,7 @@ from cxxheaderparser.types import ( TemplateTypeParam, Token, Type, + Typedef, Value, ) from cxxheaderparser.simple import ( @@ -993,3 +994,54 @@ def test_fn_w_mvreference(): ] ) ) + + +def test_msvc_conventions(): + content = """ + void __cdecl fn(); + typedef const char* (__stdcall *wglGetExtensionsStringARB_t)(HDC theDeviceContext); + """ + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + functions=[ + Function( + return_type=Type( + typename=PQName(segments=[FundamentalSpecifier(name="void")]) + ), + name=PQName(segments=[NameSpecifier(name="fn")]), + parameters=[], + msvc_convention="__cdecl", + ) + ], + typedefs=[ + Typedef( + type=Pointer( + ptr_to=FunctionType( + return_type=Pointer( + ptr_to=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="char")] + ), + const=True, + ) + ), + parameters=[ + Parameter( + type=Type( + typename=PQName( + segments=[NameSpecifier(name="HDC")] + ) + ), + name="theDeviceContext", + ) + ], + msvc_convention="__stdcall", + ) + ), + name="wglGetExtensionsStringARB_t", + ) + ], + ) + )