From 9756025e2ddd6029bc451cbcdfc5e4193f117d12 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:30:41 -0500 Subject: [PATCH 1/8] mypy: add None return types to functions that don't return a value --- cxxheaderparser/dump.py | 2 +- cxxheaderparser/gentest.py | 4 +- cxxheaderparser/parser.py | 4 +- cxxheaderparser/parserstate.py | 2 +- cxxheaderparser/simple.py | 6 +-- cxxheaderparser/visitor.py | 4 +- tests/test_attributes.py | 8 ++-- tests/test_class.py | 78 +++++++++++++++++----------------- tests/test_class_base.py | 8 ++-- tests/test_doxygen.py | 14 +++--- tests/test_enum.py | 30 ++++++------- tests/test_fn.py | 42 +++++++++--------- tests/test_friends.py | 8 ++-- tests/test_misc.py | 18 ++++---- tests/test_namespaces.py | 4 +- tests/test_operators.py | 4 +- tests/test_template.py | 28 ++++++------ tests/test_tokfmt.py | 5 ++- tests/test_typedef.py | 46 ++++++++++---------- tests/test_union.py | 4 +- tests/test_using.py | 18 ++++---- tests/test_var.py | 42 +++++++++--------- 22 files changed, 190 insertions(+), 189 deletions(-) diff --git a/cxxheaderparser/dump.py b/cxxheaderparser/dump.py index f9e194b..daa1e5c 100644 --- a/cxxheaderparser/dump.py +++ b/cxxheaderparser/dump.py @@ -9,7 +9,7 @@ from .options import ParserOptions from .simple import parse_file -def dumpmain(): +def dumpmain() -> None: parser = argparse.ArgumentParser() parser.add_argument("header") diff --git a/cxxheaderparser/gentest.py b/cxxheaderparser/gentest.py index 19bca76..ff30512 100644 --- a/cxxheaderparser/gentest.py +++ b/cxxheaderparser/gentest.py @@ -46,7 +46,7 @@ def nondefault_repr(data): return _inner_repr(data) -def gentest(infile: str, name: str, outfile: str, verbose: bool): +def gentest(infile: str, name: str, outfile: str, verbose: bool) -> None: # Goal is to allow making a unit test as easy as running this dumper # on a file and copy/pasting this into a test @@ -64,7 +64,7 @@ def gentest(infile: str, name: str, outfile: str, verbose: bool): stmt = inspect.cleandoc( f''' - def test_{name}(): + def test_{name}() -> None: content = """ {content} """ diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index dec6e33..5b7a7c2 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -544,7 +544,7 @@ class CxxParser: return TemplateDecl(params) - def _parse_template(self, tok: LexToken, doxygen: typing.Optional[str]): + def _parse_template(self, tok: LexToken, doxygen: typing.Optional[str]) -> None: template = self._parse_template_decl() @@ -2128,7 +2128,7 @@ class CxxParser: template: typing.Optional[TemplateDecl], is_typedef: bool, is_friend: bool, - ): + ) -> None: tok = self._next_token_must_be("operator") if is_typedef: diff --git a/cxxheaderparser/parserstate.py b/cxxheaderparser/parserstate.py index 0f0722b..89c892d 100644 --- a/cxxheaderparser/parserstate.py +++ b/cxxheaderparser/parserstate.py @@ -57,7 +57,7 @@ class ExternBlockState(State): super().__init__(parent) self.linkage = linkage - def _finish(self, visitor: "CxxVisitor"): + def _finish(self, visitor: "CxxVisitor") -> None: visitor.on_extern_block_end(self) diff --git a/cxxheaderparser/simple.py b/cxxheaderparser/simple.py index 4ef6f24..0e5d086 100644 --- a/cxxheaderparser/simple.py +++ b/cxxheaderparser/simple.py @@ -185,7 +185,7 @@ class SimpleCxxVisitor: namespace: NamespaceScope block: Block - def __init__(self): + def __init__(self) -> None: self.namespace = NamespaceScope("") self.block = self.namespace @@ -259,7 +259,7 @@ class SimpleCxxVisitor: ns = UsingNamespace("::".join(namespace)) self.block.using_ns.append(ns) - def on_using_alias(self, state: State, using: UsingAlias): + def on_using_alias(self, state: State, using: UsingAlias) -> None: self.block.using_alias.append(using) def on_using_declaration(self, state: State, using: UsingDecl) -> None: @@ -288,7 +288,7 @@ class SimpleCxxVisitor: def on_class_method(self, state: ClassBlockState, method: Method) -> None: self.block.methods.append(method) - def on_class_friend(self, state: ClassBlockState, friend: FriendDecl): + def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: self.block.friends.append(friend) def on_class_end(self, state: ClassBlockState) -> None: diff --git a/cxxheaderparser/visitor.py b/cxxheaderparser/visitor.py index 8afeb45..828af5a 100644 --- a/cxxheaderparser/visitor.py +++ b/cxxheaderparser/visitor.py @@ -119,7 +119,7 @@ class CxxVisitor(Protocol): using namespace std; """ - def on_using_alias(self, state: State, using: UsingAlias): + def on_using_alias(self, state: State, using: UsingAlias) -> None: """ .. code-block:: c++ @@ -171,7 +171,7 @@ class CxxVisitor(Protocol): Called when a field of a class is encountered """ - def on_class_friend(self, state: ClassBlockState, friend: FriendDecl): + def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: """ Called when a friend declaration is encountered """ diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 122bada..4f1da84 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -28,7 +28,7 @@ from cxxheaderparser.simple import ( ) -def test_attributes_everywhere(): +def test_attributes_everywhere() -> None: # TODO: someday we'll actually support storing attributes, # but for now just make sure they don't get in the way @@ -124,7 +124,7 @@ def test_attributes_everywhere(): ) -def test_attributes_gcc_enum_packed(): +def test_attributes_gcc_enum_packed() -> None: content = """ enum Wheat { w1, @@ -152,7 +152,7 @@ def test_attributes_gcc_enum_packed(): ) -def test_friendly_declspec(): +def test_friendly_declspec() -> None: content = """ struct D { friend __declspec(dllexport) void my_friend(); @@ -205,7 +205,7 @@ def test_friendly_declspec(): ) -def test_declspec_template(): +def test_declspec_template() -> None: content = """ template __declspec(deprecated("message")) diff --git a/tests/test_class.py b/tests/test_class.py index a932589..599afb0 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -38,7 +38,7 @@ from cxxheaderparser.simple import ( ) -def test_class_member_spec_1(): +def test_class_member_spec_1() -> None: content = """ class S { int d1; // non-static data member @@ -266,7 +266,7 @@ def test_class_member_spec_1(): ) -def test_class_member_spec_2(): +def test_class_member_spec_2() -> None: content = """ class M { std::size_t C; @@ -437,7 +437,7 @@ def test_class_member_spec_2(): ) -def test_class_member_spec_3(): +def test_class_member_spec_3() -> None: content = """ class S { public: @@ -513,7 +513,7 @@ def test_class_member_spec_3(): ) -def test_class_using(): +def test_class_using() -> None: content = """ class Base { protected: @@ -586,7 +586,7 @@ def test_class_using(): ) -def test_class_member_spec_6(): +def test_class_member_spec_6() -> None: content = """ struct S { template @@ -688,7 +688,7 @@ def test_class_member_spec_6(): ) -def test_class_fn_default_params(): +def test_class_fn_default_params() -> None: content = """ // clang-format off class Hen @@ -796,7 +796,7 @@ def test_class_fn_default_params(): ) -def test_class_fn_inline_virtual(): +def test_class_fn_inline_virtual() -> None: content = """ class B { public: @@ -834,7 +834,7 @@ def test_class_fn_inline_virtual(): ) -def test_class_fn_pure_virtual_const(): +def test_class_fn_pure_virtual_const() -> None: content = """ class StoneClass { virtual int getNum2() const = 0; @@ -884,7 +884,7 @@ def test_class_fn_pure_virtual_const(): ) -def test_class_fn_return_global_ns(): +def test_class_fn_return_global_ns() -> None: content = """ struct Avacado { uint8_t foo() { return 4; } @@ -935,7 +935,7 @@ def test_class_fn_return_global_ns(): ) -def test_class_ns_class(): +def test_class_ns_class() -> None: content = """ namespace ns { class N; @@ -976,7 +976,7 @@ def test_class_ns_class(): ) -def test_class_ns_w_base(): +def test_class_ns_w_base() -> None: content = """ class Herb::Cilantro : public Plant {}; """ @@ -1007,7 +1007,7 @@ def test_class_ns_w_base(): ) -def test_class_inner_class(): +def test_class_inner_class() -> None: content = """ class C { class Inner {}; @@ -1042,7 +1042,7 @@ def test_class_inner_class(): ) -def test_class_inner_fwd_class(): +def test_class_inner_fwd_class() -> None: content = """ class C { class N; @@ -1083,7 +1083,7 @@ def test_class_inner_fwd_class(): ) -def test_class_inner_var_access(): +def test_class_inner_var_access() -> None: content = """ class Bug_3488053 { public: @@ -1133,7 +1133,7 @@ def test_class_inner_var_access(): ) -def test_class_ns_and_inner(): +def test_class_ns_and_inner() -> None: content = """ namespace RoosterNamespace { class RoosterOuterClass { @@ -1275,7 +1275,7 @@ def test_class_ns_and_inner(): ) -def test_class_struct_access(): +def test_class_struct_access() -> None: content = """ struct SampleStruct { unsigned int meth(); @@ -1325,7 +1325,7 @@ def test_class_struct_access(): ) -def test_class_volatile_move_deleted_fn(): +def test_class_volatile_move_deleted_fn() -> None: content = """ struct C { void foo() volatile && = delete; @@ -1363,7 +1363,7 @@ def test_class_volatile_move_deleted_fn(): ) -def test_class_bitfield_1(): +def test_class_bitfield_1() -> None: content = """ struct S { // will usually occupy 2 bytes: @@ -1441,7 +1441,7 @@ def test_class_bitfield_1(): ) -def test_class_bitfield_2(): +def test_class_bitfield_2() -> None: content = """ struct HAL_ControlWord { int x : 1; @@ -1521,7 +1521,7 @@ def test_class_bitfield_2(): ) -def test_class_anon_struct_as_globalvar(): +def test_class_anon_struct_as_globalvar() -> None: content = """ struct { int m; @@ -1575,7 +1575,7 @@ def test_class_anon_struct_as_globalvar(): ) -def test_class_anon_struct_as_classvar(): +def test_class_anon_struct_as_classvar() -> None: content = """ struct AnonHolderClass { struct { @@ -1633,7 +1633,7 @@ def test_class_anon_struct_as_classvar(): ) -def test_initializer_with_initializer_list_1(): +def test_initializer_with_initializer_list_1() -> None: content = """ struct ComplexInit : SomeBase { ComplexInit(int i) : m_stuff{i, 2} { auto i = something(); } @@ -1730,7 +1730,7 @@ def test_initializer_with_initializer_list_1(): ) -def test_initializer_with_initializer_list_2(): +def test_initializer_with_initializer_list_2() -> None: content = """ template class future final { public: @@ -1804,7 +1804,7 @@ def test_initializer_with_initializer_list_2(): ) -def test_class_with_arrays(): +def test_class_with_arrays() -> None: content = """ const int MAX_ITEM = 7; class Bird { @@ -1875,7 +1875,7 @@ def test_class_with_arrays(): ) -def test_class_fn_inline_impl(): +def test_class_fn_inline_impl() -> None: content = """ class Monkey { private: @@ -1929,7 +1929,7 @@ def test_class_fn_inline_impl(): ) -def test_class_fn_virtual_final_override(): +def test_class_fn_virtual_final_override() -> None: content = """ struct Lemon { virtual void foo() final; @@ -2020,7 +2020,7 @@ def test_class_fn_virtual_final_override(): ) -def test_class_fn_return_class(): +def test_class_fn_return_class() -> None: content = """ class Peach { int abc; @@ -2144,7 +2144,7 @@ def test_class_fn_return_class(): ) -def test_class_fn_template_impl(): +def test_class_fn_template_impl() -> None: content = """ class Owl { private: @@ -2231,7 +2231,7 @@ def test_class_fn_template_impl(): ) -def test_class_fn_inline_template_impl(): +def test_class_fn_inline_template_impl() -> None: content = """ class Chicken { template static T Get(); @@ -2287,7 +2287,7 @@ def test_class_fn_inline_template_impl(): ) -def test_class_fn_explicit_constructors(): +def test_class_fn_explicit_constructors() -> None: content = """ class Lizzard { Lizzard(); @@ -2337,7 +2337,7 @@ def test_class_fn_explicit_constructors(): ) -def test_class_fn_default_constructor(): +def test_class_fn_default_constructor() -> None: content = """ class DefaultConstDest { public: @@ -2374,7 +2374,7 @@ def test_class_fn_default_constructor(): ) -def test_class_fn_delete_constructor(): +def test_class_fn_delete_constructor() -> None: content = """ class A { public: @@ -2408,7 +2408,7 @@ def test_class_fn_delete_constructor(): ) -def test_class_multi_vars(): +def test_class_multi_vars() -> None: content = """ class Grape { public: @@ -2580,7 +2580,7 @@ def test_class_multi_vars(): ) -def test_class_static_const_var_expr(): +def test_class_static_const_var_expr() -> None: content = """ class PandaClass { static const int CONST_A = (1 << 7) - 1; @@ -2648,7 +2648,7 @@ def test_class_static_const_var_expr(): ) -def test_class_fwd_struct(): +def test_class_fwd_struct() -> None: content = """ class PotatoClass { struct FwdStruct; @@ -2720,7 +2720,7 @@ def test_class_fwd_struct(): ) -def test_class_multi_array(): +def test_class_multi_array() -> None: content = """ struct Picture { char name[25]; @@ -2777,7 +2777,7 @@ def test_class_multi_array(): ) -def test_class_noexcept(): +def test_class_noexcept() -> None: content = """ struct Grackle { void no_noexcept(); @@ -2919,7 +2919,7 @@ def test_class_noexcept(): ) -def test_class_volatile(): +def test_class_volatile() -> None: content = """ class Foo { @@ -2960,7 +2960,7 @@ def test_class_volatile(): ) -def test_class_mutable(): +def test_class_mutable() -> None: content = """ class Foo { diff --git a/tests/test_class_base.py b/tests/test_class_base.py index 543f226..4e7a840 100644 --- a/tests/test_class_base.py +++ b/tests/test_class_base.py @@ -21,7 +21,7 @@ from cxxheaderparser.simple import ( ) -def test_class_private_base(): +def test_class_private_base() -> None: content = """ namespace Citrus { @@ -108,7 +108,7 @@ def test_class_private_base(): ) -def test_class_virtual_base(): +def test_class_virtual_base() -> None: content = """ class BaseMangoClass {}; class MangoClass : virtual public BaseMangoClass {}; @@ -148,7 +148,7 @@ def test_class_virtual_base(): ) -def test_class_multiple_base_with_virtual(): +def test_class_multiple_base_with_virtual() -> None: content = """ class BlueJay : public Bird, public virtual Food { public: @@ -193,7 +193,7 @@ def test_class_multiple_base_with_virtual(): ) -def test_class_base_specialized(): +def test_class_base_specialized() -> None: content = """ class Pea : public Vegetable { int i; diff --git a/tests/test_doxygen.py b/tests/test_doxygen.py index 7bc1472..97cbd07 100644 --- a/tests/test_doxygen.py +++ b/tests/test_doxygen.py @@ -38,7 +38,7 @@ from cxxheaderparser.simple import ( ) -def test_doxygen_class(): +def test_doxygen_class() -> None: content = """ // clang-format off @@ -107,7 +107,7 @@ def test_doxygen_class(): ) -def test_doxygen_class_template(): +def test_doxygen_class_template() -> None: content = """ // clang-format off @@ -136,7 +136,7 @@ def test_doxygen_class_template(): ) -def test_doxygen_enum(): +def test_doxygen_enum() -> None: content = """ // clang-format off @@ -182,7 +182,7 @@ def test_doxygen_enum(): ) -def test_doxygen_fn_3slash(): +def test_doxygen_fn_3slash() -> None: content = """ // clang-format off @@ -209,7 +209,7 @@ def test_doxygen_fn_3slash(): ) -def test_doxygen_fn_cstyle(): +def test_doxygen_fn_cstyle() -> None: content = """ // clang-format off @@ -238,7 +238,7 @@ def test_doxygen_fn_cstyle(): ) -def test_doxygen_var_above(): +def test_doxygen_var_above() -> None: content = """ // clang-format off @@ -267,7 +267,7 @@ def test_doxygen_var_above(): ) -def test_doxygen_var_after(): +def test_doxygen_var_after() -> None: content = """ // clang-format off diff --git a/tests/test_enum.py b/tests/test_enum.py index 8b1953f..b2fa002 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -25,7 +25,7 @@ from cxxheaderparser.simple import ( ) -def test_basic_enum(): +def test_basic_enum() -> None: content = """ enum Foo { A, @@ -48,7 +48,7 @@ def test_basic_enum(): ) -def test_enum_w_expr(): +def test_enum_w_expr() -> None: content = """ enum Foo { A = (1 / 2), @@ -85,7 +85,7 @@ def test_enum_w_expr(): ) -def test_enum_w_multiline_expr(): +def test_enum_w_multiline_expr() -> None: content = r""" // clang-format off enum Author @@ -139,7 +139,7 @@ def test_enum_w_multiline_expr(): ) -def test_basic_enum_class(): +def test_basic_enum_class() -> None: content = """ enum class BE { BEX }; """ @@ -159,7 +159,7 @@ def test_basic_enum_class(): ) -def test_basic_enum_struct(): +def test_basic_enum_struct() -> None: content = """ enum struct BE { BEX }; """ @@ -179,7 +179,7 @@ def test_basic_enum_struct(): ) -def test_enum_base(): +def test_enum_base() -> None: content = """ enum class E : int {}; """ @@ -203,7 +203,7 @@ def test_enum_base(): # instances -def test_enum_instance_1(): +def test_enum_instance_1() -> None: content = """ enum class BE { BEX } be1; """ @@ -233,7 +233,7 @@ def test_enum_instance_1(): ) -def test_enum_instance_2(): +def test_enum_instance_2() -> None: content = """ enum class BE { BEX } be1, *be2; """ @@ -277,7 +277,7 @@ def test_enum_instance_2(): # bases in namespaces -def test_enum_base_in_ns(): +def test_enum_base_in_ns() -> None: content = """ namespace EN { typedef int EINT; @@ -322,7 +322,7 @@ def test_enum_base_in_ns(): # forward declarations -def test_enum_fwd(): +def test_enum_fwd() -> None: content = """ enum class BE1; enum class BE2 : EN::EINT; @@ -350,7 +350,7 @@ def test_enum_fwd(): ) -def test_enum_private_in_class(): +def test_enum_private_in_class() -> None: content = """ class C { @@ -383,7 +383,7 @@ def test_enum_private_in_class(): ) -def test_enum_public_in_class(): +def test_enum_public_in_class() -> None: content = """ class C { @@ -417,7 +417,7 @@ def test_enum_public_in_class(): ) -def test_default_enum(): +def test_default_enum() -> None: content = """ class A { enum { @@ -497,7 +497,7 @@ def test_default_enum(): ) -def test_enum_template_vals(): +def test_enum_template_vals() -> None: content = """ enum { IsRandomAccess = std::is_base_of None: content = """ enum E { VALUE, diff --git a/tests/test_fn.py b/tests/test_fn.py index c1daef0..c5688bd 100644 --- a/tests/test_fn.py +++ b/tests/test_fn.py @@ -31,7 +31,7 @@ from cxxheaderparser.simple import ( ) -def test_fn_returns_class(): +def test_fn_returns_class() -> None: content = """ class X *fn1(); struct Y fn2(); @@ -77,7 +77,7 @@ def test_fn_returns_class(): ) -def test_fn_returns_typename(): +def test_fn_returns_typename() -> None: content = """ typename ns::X fn(); """ @@ -104,7 +104,7 @@ def test_fn_returns_typename(): ) -def test_fn_returns_typename_const(): +def test_fn_returns_typename_const() -> None: content = """ const typename ns::X fn(); """ @@ -132,7 +132,7 @@ def test_fn_returns_typename_const(): ) -def test_fn_pointer_params(): +def test_fn_pointer_params() -> None: content = """ int fn1(int *); int fn2(int *p); @@ -201,7 +201,7 @@ def test_fn_pointer_params(): ) -def test_fn_void_is_no_params(): +def test_fn_void_is_no_params() -> None: content = """ int fn(void); """ @@ -222,7 +222,7 @@ def test_fn_void_is_no_params(): ) -def test_fn_array_param(): +def test_fn_array_param() -> None: content = """ void fn(int array[]); """ @@ -255,7 +255,7 @@ def test_fn_array_param(): ) -def test_fn_typename_param(): +def test_fn_typename_param() -> None: content = """ void MethodA(const mynamespace::SomeObject &x, typename mynamespace::SomeObject * = 0); @@ -307,7 +307,7 @@ def test_fn_typename_param(): ) -def test_fn_weird_refs(): +def test_fn_weird_refs() -> None: content = """ int aref(int(&x)); void ptr_ref(int(*&name)); @@ -382,7 +382,7 @@ def test_fn_weird_refs(): ) -def test_fn_too_many_parens(): +def test_fn_too_many_parens() -> None: content = """ int fn1(int (x)); void (fn2 (int (*const (name)))); @@ -439,7 +439,7 @@ void (__stdcall * fn) """ -def test_fn_same_line(): +def test_fn_same_line() -> None: # multiple functions on the same line content = """ void fn1(), fn2(); @@ -487,7 +487,7 @@ def test_fn_same_line(): ) -def test_fn_auto_template(): +def test_fn_auto_template() -> None: content = """ template auto add(T t, U u) { return t + u; } @@ -527,7 +527,7 @@ def test_fn_auto_template(): ) -def test_fn_template_ptr(): +def test_fn_template_ptr() -> None: content = """ std::vector *fn(std::vector *ps); """ @@ -607,7 +607,7 @@ def test_fn_template_ptr(): ) -def test_fn_with_impl(): +def test_fn_with_impl() -> None: content = """ // clang-format off void termite(void) @@ -634,7 +634,7 @@ def test_fn_with_impl(): ) -def test_fn_return_std_function(): +def test_fn_return_std_function() -> None: content = """ std::function fn(); """ @@ -700,7 +700,7 @@ def test_fn_return_std_function(): assert data2 == expected -def test_fn_return_std_function_trailing(): +def test_fn_return_std_function_trailing() -> None: content = """ std::functionint> fn(); """ @@ -759,7 +759,7 @@ def test_fn_return_std_function_trailing(): ) -def test_fn_trailing_return_simple(): +def test_fn_trailing_return_simple() -> None: content = """ auto fn() -> int; """ @@ -781,7 +781,7 @@ def test_fn_trailing_return_simple(): ) -def test_fn_trailing_return_std_function(): +def test_fn_trailing_return_std_function() -> None: content = """ auto fn() -> std::function; """ @@ -828,7 +828,7 @@ def test_fn_trailing_return_std_function(): ) -def test_inline_volatile_fn(): +def test_inline_volatile_fn() -> None: content = """ inline int Standard_Atomic_Increment (volatile int* theValue); """ @@ -864,7 +864,7 @@ def test_inline_volatile_fn(): ) -def test_method_w_reference(): +def test_method_w_reference() -> None: content = """ struct StreamBuffer { @@ -947,7 +947,7 @@ def test_method_w_reference(): ) -def test_fn_w_mvreference(): +def test_fn_w_mvreference() -> None: content = """ void fn1(int && (*)(int)); @@ -996,7 +996,7 @@ def test_fn_w_mvreference(): ) -def test_msvc_conventions(): +def test_msvc_conventions() -> None: content = """ void __cdecl fn(); typedef const char* (__stdcall *wglGetExtensionsStringARB_t)(HDC theDeviceContext); diff --git a/tests/test_friends.py b/tests/test_friends.py index c600e1b..8355c33 100644 --- a/tests/test_friends.py +++ b/tests/test_friends.py @@ -23,7 +23,7 @@ from cxxheaderparser.simple import ( ) # friends -def test_various_friends(): +def test_various_friends() -> None: content = """ class FX { public: @@ -170,7 +170,7 @@ def test_various_friends(): ) -def test_more_friends(): +def test_more_friends() -> None: content = """ template struct X { static int x; }; @@ -285,7 +285,7 @@ def test_more_friends(): ) -def test_friend_type_no_class(): +def test_friend_type_no_class() -> None: content = """ class DogClass; class CatClass { @@ -327,7 +327,7 @@ def test_friend_type_no_class(): ) -def test_friend_with_impl(): +def test_friend_with_impl() -> None: content = """ // clang-format off class Garlic { diff --git a/tests/test_misc.py b/tests/test_misc.py index a9dd0d2..b90fd96 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -28,7 +28,7 @@ from cxxheaderparser.simple import ( # -def test_define(): +def test_define() -> None: content = """ #define simple #define complex(thing) stuff(thing) @@ -45,7 +45,7 @@ def test_define(): ) -def test_includes(): +def test_includes() -> None: content = """ #include #include "local.h" @@ -55,7 +55,7 @@ def test_includes(): assert data == ParsedData(includes=[Include(""), Include('"local.h"')]) -def test_pragma(): +def test_pragma() -> None: content = """ #pragma once @@ -71,7 +71,7 @@ def test_pragma(): # -def test_extern_c(): +def test_extern_c() -> None: content = """ extern "C" { int x; @@ -101,7 +101,7 @@ def test_extern_c(): ) -def test_misc_extern_inline(): +def test_misc_extern_inline() -> None: content = """ extern "C++" { inline HAL_Value HAL_GetSimValue(HAL_SimValueHandle handle) { @@ -143,7 +143,7 @@ def test_misc_extern_inline(): # -def test_static_assert_1(): +def test_static_assert_1() -> None: # static_assert should be ignored content = """ static_assert(x == 1); @@ -153,7 +153,7 @@ def test_static_assert_1(): assert data == ParsedData() -def test_static_assert_2(): +def test_static_assert_2() -> None: # static_assert should be ignored content = """ static_assert(sizeof(int) == 4, @@ -165,7 +165,7 @@ def test_static_assert_2(): assert data == ParsedData() -def test_comment_eof(): +def test_comment_eof() -> None: content = """ namespace a {} // namespace a""" data = parse_string(content, cleandoc=True) @@ -175,7 +175,7 @@ def test_comment_eof(): ) -def test_final(): +def test_final() -> None: content = """ // ok here int fn(const int final); diff --git a/tests/test_namespaces.py b/tests/test_namespaces.py index 2b99f33..74f7f57 100644 --- a/tests/test_namespaces.py +++ b/tests/test_namespaces.py @@ -16,7 +16,7 @@ from cxxheaderparser.simple import ( ) -def test_dups_in_different_ns(): +def test_dups_in_different_ns() -> None: content = """ namespace { @@ -58,7 +58,7 @@ def test_dups_in_different_ns(): ) -def test_correct_ns(): +def test_correct_ns() -> None: content = """ namespace a::b::c { int i1; diff --git a/tests/test_operators.py b/tests/test_operators.py index ae060ce..8d3bb60 100644 --- a/tests/test_operators.py +++ b/tests/test_operators.py @@ -18,7 +18,7 @@ from cxxheaderparser.simple import ( ) -def test_class_operators(): +def test_class_operators() -> None: content = r""" class OperatorClass { public: @@ -554,7 +554,7 @@ def test_class_operators(): ) -def test_conversion_operators(): +def test_conversion_operators() -> None: content = """ class Foo diff --git a/tests/test_template.py b/tests/test_template.py index 85e0e72..6a9e246 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -29,7 +29,7 @@ from cxxheaderparser.types import ( from cxxheaderparser.simple import ClassScope, NamespaceScope, ParsedData, parse_string -def test_template_base_template_ns(): +def test_template_base_template_ns() -> None: content = """ class A : public B::C {}; """ @@ -89,7 +89,7 @@ def test_template_base_template_ns(): ) -def test_template_non_type_various(): +def test_template_non_type_various() -> None: content = """ // simple non-type template parameter template struct S { int a[N]; }; @@ -257,7 +257,7 @@ def test_template_non_type_various(): ) -def test_template_dependent_nontype_default(): +def test_template_dependent_nontype_default() -> None: content = """ template class X; """ @@ -293,7 +293,7 @@ def test_template_dependent_nontype_default(): ) -def test_template_optional_names(): +def test_template_optional_names() -> None: content = """ template class My_vector; template struct My_op_functor; @@ -337,7 +337,7 @@ def test_template_optional_names(): ) -def test_template_template_template(): +def test_template_template_template() -> None: content = """ template struct eval; // primary template @@ -438,7 +438,7 @@ def test_template_template_template(): ) -def test_template_static_var(): +def test_template_static_var() -> None: content = """ template struct X { @@ -510,7 +510,7 @@ def test_template_static_var(): ) -def test_template_fn_template(): +def test_template_fn_template() -> None: content = """ class S { template StringRef copy(Allocator &A) const { @@ -574,7 +574,7 @@ def test_template_fn_template(): ) -def test_template_fn_param_initializer(): +def test_template_fn_param_initializer() -> None: content = """ template void fn(something s = something{1, 2, 3}); @@ -658,7 +658,7 @@ def test_template_fn_param_initializer(): ) -def test_template_huge(): +def test_template_huge() -> None: content = """ // clang-format off class AlmondClass @@ -872,7 +872,7 @@ def test_template_huge(): ) -def test_template_specialized(): +def test_template_specialized() -> None: content = """ template <> class FruitFly : public Fly {}; """ @@ -920,7 +920,7 @@ def test_template_specialized(): ) -def test_template_class_defaults(): +def test_template_class_defaults() -> None: content = """ template > @@ -1068,7 +1068,7 @@ def test_template_class_defaults(): ) -def test_template_many_packs(): +def test_template_many_packs() -> None: content = """ // clang-format off @@ -1651,7 +1651,7 @@ def test_template_many_packs(): ) -def test_template_specialized_fn_typename(): +def test_template_specialized_fn_typename() -> None: content = """ // clang-format off struct T{}; @@ -1723,7 +1723,7 @@ def test_template_specialized_fn_typename(): ) -def test_template_specialized_fn_typename_template(): +def test_template_specialized_fn_typename_template() -> None: content = """ // clang-format off template diff --git a/tests/test_tokfmt.py b/tests/test_tokfmt.py index cff0f1a..ba245c3 100644 --- a/tests/test_tokfmt.py +++ b/tests/test_tokfmt.py @@ -2,6 +2,7 @@ import pytest from cxxheaderparser.lexer import Lexer from cxxheaderparser.tokfmt import tokfmt +from cxxheaderparser.types import Token @pytest.mark.parametrize( @@ -34,7 +35,7 @@ from cxxheaderparser.tokfmt import tokfmt "operator>=", ], ) -def test_tokfmt(instr): +def test_tokfmt(instr: str) -> None: """ Each input string is exactly what the output of tokfmt should be """ @@ -47,6 +48,6 @@ def test_tokfmt(instr): if not tok: break - toks.append(tok) + toks.append(Token(tok.value, tok.type)) assert tokfmt(toks) == instr diff --git a/tests/test_typedef.py b/tests/test_typedef.py index a1819e3..71bd0c5 100644 --- a/tests/test_typedef.py +++ b/tests/test_typedef.py @@ -26,7 +26,7 @@ from cxxheaderparser.types import ( from cxxheaderparser.simple import ClassScope, NamespaceScope, ParsedData, parse_string -def test_simple_typedef(): +def test_simple_typedef() -> None: content = """ typedef std::vector IntVector; """ @@ -68,7 +68,7 @@ def test_simple_typedef(): ) -def test_struct_typedef_1(): +def test_struct_typedef_1() -> None: content = """ typedef struct { int m; @@ -122,7 +122,7 @@ def test_struct_typedef_1(): ) -def test_struct_typedef_2(): +def test_struct_typedef_2() -> None: content = """ typedef struct { int m; @@ -176,7 +176,7 @@ def test_struct_typedef_2(): ) -def test_typedef_array(): +def test_typedef_array() -> None: content = """ typedef char TenCharArray[10]; """ @@ -201,7 +201,7 @@ def test_typedef_array(): ) -def test_typedef_array_of_struct(): +def test_typedef_array_of_struct() -> None: content = """ typedef struct{} tx[3], ty; """ @@ -243,7 +243,7 @@ def test_typedef_array_of_struct(): ) -def test_typedef_class_w_base(): +def test_typedef_class_w_base() -> None: content = """ typedef class XX : public F {} G; """ @@ -280,7 +280,7 @@ def test_typedef_class_w_base(): ) -def test_complicated_typedef(): +def test_complicated_typedef() -> None: content = """ typedef int int_t, *intp_t, (&fp)(int, ulong), arr_t[10]; """ @@ -345,7 +345,7 @@ def test_complicated_typedef(): ) -def test_typedef_c_struct_idiom(): +def test_typedef_c_struct_idiom() -> None: content = """ // common C idiom to avoid having to write "struct S" typedef struct {int a; int b;} S, *pS; @@ -407,7 +407,7 @@ def test_typedef_c_struct_idiom(): ) -def test_typedef_struct_same_name(): +def test_typedef_struct_same_name() -> None: content = """ typedef struct Fig { int a; @@ -451,7 +451,7 @@ def test_typedef_struct_same_name(): ) -def test_typedef_struct_w_enum(): +def test_typedef_struct_w_enum() -> None: content = """ typedef struct { enum BeetEnum : int { FAIL = 0, PASS = 1 }; @@ -502,7 +502,7 @@ def test_typedef_struct_w_enum(): ) -def test_typedef_union(): +def test_typedef_union() -> None: content = """ typedef union apricot_t { int i; @@ -569,7 +569,7 @@ def test_typedef_union(): ) -def test_typedef_fnptr(): +def test_typedef_fnptr() -> None: content = """ typedef void *(*fndef)(int); """ @@ -606,7 +606,7 @@ def test_typedef_fnptr(): ) -def test_typedef_const(): +def test_typedef_const() -> None: content = """ typedef int theint, *const ptheint; """ @@ -635,7 +635,7 @@ def test_typedef_const(): ) -def test_enum_typedef_1(): +def test_enum_typedef_1() -> None: content = """ typedef enum {} E; """ @@ -661,7 +661,7 @@ def test_enum_typedef_1(): ) -def test_enum_typedef_2(): +def test_enum_typedef_2() -> None: content = """ typedef enum { E1 } BE; """ @@ -687,7 +687,7 @@ def test_enum_typedef_2(): ) -def test_enum_typedef_3(): +def test_enum_typedef_3() -> None: content = """ typedef enum { E1, E2, } E; """ @@ -713,7 +713,7 @@ def test_enum_typedef_3(): ) -def test_enum_typedef_3_1(): +def test_enum_typedef_3_1() -> None: content = """ typedef enum { E1 } * PBE; """ @@ -743,7 +743,7 @@ def test_enum_typedef_3_1(): ) -def test_enum_typedef_4(): +def test_enum_typedef_4() -> None: content = """ typedef enum { E1 } * PBE, BE; """ @@ -779,7 +779,7 @@ def test_enum_typedef_4(): ) -def test_enum_typedef_5(): +def test_enum_typedef_5() -> None: content = """ typedef enum { E1 } BE, *PBE; """ @@ -815,7 +815,7 @@ def test_enum_typedef_5(): ) -def test_enum_typedef_fwd(): +def test_enum_typedef_fwd() -> None: content = """ typedef enum BE BET; """ @@ -837,7 +837,7 @@ def test_enum_typedef_fwd(): ) -def test_typedef_enum_expr(): +def test_typedef_enum_expr() -> None: content = """ typedef enum { StarFruit = (2 + 2) / 2 } Carambola; """ @@ -878,7 +878,7 @@ def test_typedef_enum_expr(): ) -def test_volatile_typedef(): +def test_volatile_typedef() -> None: content = """ typedef volatile signed short vint16; """ @@ -901,7 +901,7 @@ def test_volatile_typedef(): ) -def test_function_typedef(): +def test_function_typedef() -> None: content = """ typedef void fn(int); typedef auto fntype(int) -> int; diff --git a/tests/test_union.py b/tests/test_union.py index d9a660f..ffbb9c9 100644 --- a/tests/test_union.py +++ b/tests/test_union.py @@ -17,7 +17,7 @@ from cxxheaderparser.simple import ( ) -def test_union_basic(): +def test_union_basic() -> None: content = """ struct HAL_Value { @@ -86,7 +86,7 @@ def test_union_basic(): ) -def test_union_anon_in_struct(): +def test_union_anon_in_struct() -> None: content = """ struct Outer { union { diff --git a/tests/test_using.py b/tests/test_using.py index 616257a..ca1d76e 100644 --- a/tests/test_using.py +++ b/tests/test_using.py @@ -30,7 +30,7 @@ from cxxheaderparser.simple import ( ) -def test_using_namespace(): +def test_using_namespace() -> None: content = """ using namespace foo; using namespace foo::bar; @@ -52,7 +52,7 @@ def test_using_namespace(): ) -def test_using_declaration(): +def test_using_declaration() -> None: content = """ using ::foo; using foo::bar; @@ -104,7 +104,7 @@ def test_using_declaration(): # alias-declaration -def test_alias_declaration_1(): +def test_alias_declaration_1() -> None: content = """ using alias = foo; """ @@ -122,7 +122,7 @@ def test_alias_declaration_1(): ) -def test_alias_declaration_2(): +def test_alias_declaration_2() -> None: content = """ template using alias = foo; """ @@ -164,7 +164,7 @@ def test_alias_declaration_2(): ) -def test_alias_declaration_3(): +def test_alias_declaration_3() -> None: content = """ using alias = ::foo::bar; """ @@ -190,7 +190,7 @@ def test_alias_declaration_3(): ) -def test_alias_declaration_4(): +def test_alias_declaration_4() -> None: content = """ template using alias = ::foo::bar; """ @@ -234,7 +234,7 @@ def test_alias_declaration_4(): ) -def test_alias_declaration_5(): +def test_alias_declaration_5() -> None: content = """ using alias = foo::bar; """ @@ -259,7 +259,7 @@ def test_alias_declaration_5(): ) -def test_alias_declaration_6(): +def test_alias_declaration_6() -> None: content = """ template using alias = foo::bar; """ @@ -302,7 +302,7 @@ def test_alias_declaration_6(): ) -def test_using_many_things(): +def test_using_many_things() -> None: content = """ // clang-format off diff --git a/tests/test_var.py b/tests/test_var.py index 3621959..488ad10 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -22,7 +22,7 @@ from cxxheaderparser.types import ( from cxxheaderparser.simple import ClassScope, NamespaceScope, ParsedData, parse_string -def test_var_unixwiz_ridiculous(): +def test_var_unixwiz_ridiculous() -> None: # http://unixwiz.net/techtips/reading-cdecl.html # # .. "we have no idea how this variable is useful, but at least we can @@ -73,7 +73,7 @@ def test_var_unixwiz_ridiculous(): ) -def test_var_ptr_to_array15_of_ptr_to_int(): +def test_var_ptr_to_array15_of_ptr_to_int() -> None: content = """ int *(*crocodile)[15]; """ @@ -102,7 +102,7 @@ def test_var_ptr_to_array15_of_ptr_to_int(): ) -def test_var_ref_to_array(): +def test_var_ref_to_array() -> None: content = """ int abase[3]; int (&aname)[3] = abase; @@ -140,7 +140,7 @@ def test_var_ref_to_array(): ) -def test_var_ptr_to_array(): +def test_var_ptr_to_array() -> None: content = """ int zz, (*aname)[3] = &abase; """ @@ -174,7 +174,7 @@ def test_var_ptr_to_array(): ) -def test_var_multi_1(): +def test_var_multi_1() -> None: content = """ int zz, (&aname)[3] = abase; """ @@ -208,7 +208,7 @@ def test_var_multi_1(): ) -def test_var_array_of_fnptr_varargs(): +def test_var_array_of_fnptr_varargs() -> None: content = """ void (*a3[3])(int, ...); """ @@ -249,7 +249,7 @@ def test_var_array_of_fnptr_varargs(): ) -def test_var_double_fnptr_varargs(): +def test_var_double_fnptr_varargs() -> None: content = """ void (*(*a4))(int, ...); """ @@ -289,7 +289,7 @@ def test_var_double_fnptr_varargs(): ) -def test_var_fnptr_voidstar(): +def test_var_fnptr_voidstar() -> None: content = """ void(*(*a5)(int)); """ @@ -326,7 +326,7 @@ def test_var_fnptr_voidstar(): ) -def test_var_fnptr_moreparens(): +def test_var_fnptr_moreparens() -> None: content = """ void (*x)(int(p1), int); """ @@ -384,7 +384,7 @@ def test_var_fnptr_moreparens(): # Means "const pointer to pointer to char" -def test_var_ptr_to_const_ptr_to_char(): +def test_var_ptr_to_const_ptr_to_char() -> None: content = """ char *const *p; """ @@ -411,7 +411,7 @@ def test_var_ptr_to_const_ptr_to_char(): ) -def test_var_const_ptr_to_ptr_to_char(): +def test_var_const_ptr_to_ptr_to_char() -> None: content = """ char **const p; """ @@ -438,7 +438,7 @@ def test_var_const_ptr_to_ptr_to_char(): ) -def test_var_array_initializer1(): +def test_var_array_initializer1() -> None: content = """ int x[3]{1, 2, 3}; """ @@ -472,7 +472,7 @@ def test_var_array_initializer1(): ) -def test_var_array_initializer2(): +def test_var_array_initializer2() -> None: content = """ int x[3] = {1, 2, 3}; """ @@ -506,7 +506,7 @@ def test_var_array_initializer2(): ) -def test_var_extern_c(): +def test_var_extern_c() -> None: content = """ extern "C" int x; """ @@ -528,7 +528,7 @@ def test_var_extern_c(): ) -def test_var_ns_1(): +def test_var_ns_1() -> None: content = """ int N::x; """ @@ -550,7 +550,7 @@ def test_var_ns_1(): ) -def test_var_ns_2(): +def test_var_ns_2() -> None: content = """ int N::x = 4; """ @@ -573,7 +573,7 @@ def test_var_ns_2(): ) -def test_var_ns_3(): +def test_var_ns_3() -> None: content = """ int N::x{4}; """ @@ -598,7 +598,7 @@ def test_var_ns_3(): ) -def test_var_static_struct(): +def test_var_static_struct() -> None: content = """ constexpr static struct SS {} s; """ @@ -631,7 +631,7 @@ def test_var_static_struct(): ) -def test_var_constexpr_enum(): +def test_var_constexpr_enum() -> None: content = """ constexpr enum E { EE } e = EE; """ @@ -663,7 +663,7 @@ def test_var_constexpr_enum(): ) -def test_var_fnptr_in_class(): +def test_var_fnptr_in_class() -> None: content = """ struct DriverFuncs { void *(*init)(); @@ -747,7 +747,7 @@ def test_var_fnptr_in_class(): ) -def test_var_extern(): +def test_var_extern() -> None: content = """ extern int externVar; """ From 36d23c37bd1161e058c144dae42249bd1f77108b Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:41:34 -0500 Subject: [PATCH 2/8] mypy: add trivial annotations to functions that don't change logic --- cxxheaderparser/__init__.py | 2 +- cxxheaderparser/gentest.py | 7 ++++--- cxxheaderparser/lexer.py | 34 ++++++++++++++++++++-------------- cxxheaderparser/parser.py | 14 ++++++++------ cxxheaderparser/simple.py | 2 +- cxxheaderparser/tokfmt.py | 2 +- 6 files changed, 35 insertions(+), 26 deletions(-) diff --git a/cxxheaderparser/__init__.py b/cxxheaderparser/__init__.py index 99e3037..0c7950a 100644 --- a/cxxheaderparser/__init__.py +++ b/cxxheaderparser/__init__.py @@ -1,4 +1,4 @@ try: - from .version import __version__ + from .version import __version__ # type: ignore except ImportError: __version__ = "master" diff --git a/cxxheaderparser/gentest.py b/cxxheaderparser/gentest.py index ff30512..d2d8f25 100644 --- a/cxxheaderparser/gentest.py +++ b/cxxheaderparser/gentest.py @@ -2,12 +2,13 @@ import argparse import dataclasses import inspect import subprocess +import typing from .options import ParserOptions -from .simple import parse_string +from .simple import parse_string, ParsedData -def nondefault_repr(data): +def nondefault_repr(data: ParsedData) -> str: """ Similar to the default dataclass repr, but exclude any default parameters or parameters with compare=False @@ -17,7 +18,7 @@ def nondefault_repr(data): get_fields = dataclasses.fields MISSING = dataclasses.MISSING - def _inner_repr(o) -> str: + def _inner_repr(o: typing.Any) -> str: if is_dataclass(o): vals = [] for f in get_fields(o): diff --git a/cxxheaderparser/lexer.py b/cxxheaderparser/lexer.py index ccf6f08..2bfdc08 100644 --- a/cxxheaderparser/lexer.py +++ b/cxxheaderparser/lexer.py @@ -42,6 +42,9 @@ class LexToken(Protocol): #: Location token was found at location: Location + #: private + lexer: "Lexer" + PhonyEnding: LexToken = lex.LexToken() # type: ignore PhonyEnding.type = "PLACEHOLDER" @@ -183,13 +186,13 @@ class Lexer: t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" - def t_NAME(self, t): + def t_NAME(self, t: LexToken) -> LexToken: r"[A-Za-z_~][A-Za-z0-9_]*" if t.value in self.keywords: t.type = t.value return t - def t_PRECOMP_MACRO(self, t): + def t_PRECOMP_MACRO(self, t: LexToken) -> typing.Optional[LexToken]: r"\#.*" m = _line_re.match(t.value) if m: @@ -200,11 +203,11 @@ class Lexer: self.filename = filename self.line_offset = 1 + self.lex.lineno - int(m.group(1)) - + return None else: return t - def t_COMMENT_SINGLELINE(self, t): + def t_COMMENT_SINGLELINE(self, t: LexToken) -> LexToken: r"\/\/.*\n?" if t.value.startswith("///") or t.value.startswith("//!"): self.comments.append(t.value.lstrip("\t ").rstrip("\n")) @@ -227,7 +230,7 @@ class Lexer: t_STRING_LITERAL = r'"([^"\\]|\\.)*"' # Found at http://ostermiller.org/findcomment.html - def t_COMMENT_MULTILINE(self, t): + def t_COMMENT_MULTILINE(self, t: LexToken) -> LexToken: r"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/\n?" if t.value.startswith("/**") or t.value.startswith("/*!"): # not sure why, but get double new lines @@ -238,19 +241,20 @@ class Lexer: t.lexer.lineno += t.value.count("\n") return t - def t_NEWLINE(self, t): + def t_NEWLINE(self, t: LexToken) -> LexToken: r"\n+" t.lexer.lineno += len(t.value) del self.comments[:] return t - def t_error(self, v): - print("Lex error: ", v) + def t_error(self, t: LexToken) -> None: + print("Lex error: ", t) _lexer = None lex: lex.Lexer + lineno: int - def __new__(cls, *args, **kwargs): + def __new__(cls, *args, **kwargs) -> "Lexer": # only build the lexer once inst = super().__new__(cls) if cls._lexer is None: @@ -261,14 +265,14 @@ class Lexer: return inst def __init__(self, filename: typing.Optional[str] = None): - self.input = self.lex.input + self.input: typing.Callable[[str], None] = self.lex.input # For tracking current file/line position self.filename = filename self.line_offset = 0 - self.filenames = [] - self._filenames_set = set() + self.filenames: typing.List[str] = [] + self._filenames_set: typing.Set[str] = set() if self.filename: self.filenames.append(filename) @@ -339,13 +343,15 @@ class Lexer: _discard_types = {"NEWLINE", "COMMENT_SINGLELINE", "COMMENT_MULTILINE"} - def _token_limit_exceeded(self): + def _token_limit_exceeded(self) -> typing.NoReturn: from .errors import CxxParseError raise CxxParseError("no more tokens left in this group") @contextlib.contextmanager - def set_group_of_tokens(self, toks: typing.List[LexToken]): + def set_group_of_tokens( + self, toks: typing.List[LexToken] + ) -> typing.Generator[typing.Deque[LexToken], None, None]: # intended for use when you have a set of tokens that you know # must be consumed, such as a paren grouping or some type of # lookahead case diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index 5b7a7c2..f6ece2b 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -93,9 +93,9 @@ class CxxParser: self.verbose = True if self.options.verbose else False if self.verbose: - def debug_print(fmt: str, *args: typing.Any): + def debug_print(fmt: str, *args: typing.Any) -> None: fmt = f"[%4d] {fmt}" - args = (inspect.currentframe().f_back.f_lineno,) + args + args = (inspect.currentframe().f_back.f_lineno,) + args # type: ignore print(fmt % args) self.debug_print = debug_print @@ -135,7 +135,7 @@ class CxxParser: # def _parse_error( - self, tok: typing.Optional[LexToken], expected="" + self, tok: typing.Optional[LexToken], expected: str = "" ) -> CxxParseError: if not tok: # common case after a failed token_if @@ -982,7 +982,7 @@ class CxxParser: template: typing.Optional[TemplateDecl], typedef: bool, location: Location, - props: typing.Dict[str, LexToken], + mods: ParsedTypeModifiers, ) -> None: """ class_specifier: class_head "{" [member_specification] "}" @@ -1038,7 +1038,7 @@ class CxxParser: typename, bases, template, explicit, final, doxygen, self._current_access ) state = self._push_state( - ClassBlockState, clsdecl, default_access, typedef, props + ClassBlockState, clsdecl, default_access, typedef, mods ) state.location = location self.visitor.on_class_start(state) @@ -1691,6 +1691,7 @@ class CxxParser: if not isinstance(pqname.segments[-1], NameSpecifier): raise self._parse_error(None) + props: typing.Dict props = dict.fromkeys(mods.both.keys(), True) if msvc_convention: props["msvc_convention"] = msvc_convention.value @@ -2011,6 +2012,7 @@ class CxxParser: toks = [] # On entry we only have the base type, decorate it + dtype: typing.Optional[DecoratedType] dtype = self._parse_cv_ptr(parsed_type) state = self.state @@ -2145,7 +2147,7 @@ class CxxParser: self._next_token_must_be("(") # make our own pqname/op here - segments = [NameSpecifier("operator")] + segments: typing.List[PQNameSegment] = [NameSpecifier("operator")] pqname = PQName(segments) op = "conversion" diff --git a/cxxheaderparser/simple.py b/cxxheaderparser/simple.py index 0e5d086..655bacc 100644 --- a/cxxheaderparser/simple.py +++ b/cxxheaderparser/simple.py @@ -298,7 +298,7 @@ class SimpleCxxVisitor: def parse_string( content: str, *, - filename="", + filename: str = "", options: typing.Optional[ParserOptions] = None, cleandoc: bool = False, ) -> ParsedData: diff --git a/cxxheaderparser/tokfmt.py b/cxxheaderparser/tokfmt.py index ada6790..94b241e 100644 --- a/cxxheaderparser/tokfmt.py +++ b/cxxheaderparser/tokfmt.py @@ -56,7 +56,7 @@ if __name__ == "__main__": lexer = Lexer(args.header) with open(lexer.filename) as fp: - lexer.input(fp.read()) + lexer.input(fp.read()) # type: ignore toks: typing.List[Token] = [] while True: From 2eb13496fa9cc3c325c6e7ac434cae1cb9aceb5a Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:45:06 -0500 Subject: [PATCH 3/8] mypy: function return types marked as optional Only methods can have optional return types, but it has to be applied to function as well as method, otherwise mypy complains. I considered making a special return type for constructors/destructors, but it seemed to just make things too messy. Time will tell if this was the right decision. --- cxxheaderparser/parser.py | 7 ++++++- cxxheaderparser/types.py | 7 +++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index f6ece2b..1ec3197 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -1749,6 +1749,7 @@ class CxxParser: return method.has_body or method.has_trailing_return else: + assert return_type is not None fn = Function( return_type, pqname, @@ -1778,8 +1779,12 @@ class CxxParser: if fn.template: raise CxxParseError("typedef function may not have a template") + return_type = fn.return_type + if return_type is None: + raise CxxParseError("typedef function must have return type") + fntype = FunctionType( - fn.return_type, + return_type, fn.parameters, fn.vararg, fn.has_trailing_return, diff --git a/cxxheaderparser/types.py b/cxxheaderparser/types.py index 8354427..7973b69 100644 --- a/cxxheaderparser/types.py +++ b/cxxheaderparser/types.py @@ -468,7 +468,9 @@ class Function: A function declaration, potentially with the function body """ - return_type: DecoratedType + #: Only constructors and destructors don't have a return type + return_type: typing.Optional[DecoratedType] + name: PQName parameters: typing.List[Parameter] @@ -510,9 +512,6 @@ class Method(Function): A method declaration, potentially with the method body """ - #: constructors and destructors don't have a return type - return_type: typing.Optional[DecoratedType] - access: str = "" const: bool = False From b05b1b16c1d7c3b28972696c22532e74a2b3124e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:48:49 -0500 Subject: [PATCH 4/8] mypy: add assertions/logic to ensure that ClassScope/NamespaceScope are correct --- cxxheaderparser/parser.py | 20 +++++++++++++++----- cxxheaderparser/simple.py | 8 ++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index 1ec3197..0e9548c 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -1133,7 +1133,11 @@ class CxxParser: state = self.state state.location = location - is_class_block = isinstance(state, ClassBlockState) + if isinstance(state, ClassBlockState): + is_class_block = True + class_state = state + else: + is_class_block = False name = None bits = None default = None @@ -1207,7 +1211,7 @@ class CxxParser: doxygen=doxygen, **props, ) - self.visitor.on_class_field(state, f) + self.visitor.on_class_field(class_state, f) else: v = Variable( pqname, dtype, default, doxygen=doxygen, template=template, **props @@ -1703,6 +1707,8 @@ class CxxParser: params, vararg = self._parse_parameters() if is_class_block and not is_typedef: + assert isinstance(state, ClassBlockState) + props.update(dict.fromkeys(mods.meths.keys(), True)) method: Method @@ -2111,6 +2117,8 @@ class CxxParser: if not self.lex.token_if(";"): raise self._parse_error(None) + assert isinstance(state, ClassBlockState) + fwd = ForwardDecl( parsed_type.typename, template, @@ -2283,12 +2291,14 @@ class CxxParser: fdecl = ForwardDecl( parsed_type.typename, template, doxygen, access=self._current_access ) - self.state.location = location + state = self.state + state.location = location if is_friend: + assert isinstance(state, ClassBlockState) friend = FriendDecl(cls=fdecl) - self.visitor.on_class_friend(self.state, friend) + self.visitor.on_class_friend(state, friend) else: - self.visitor.on_forward_decl(self.state, fdecl) + self.visitor.on_forward_decl(state, fdecl) return True tok = self.lex.token_if_in_set(self._class_enum_stage2) diff --git a/cxxheaderparser/simple.py b/cxxheaderparser/simple.py index 655bacc..de9389c 100644 --- a/cxxheaderparser/simple.py +++ b/cxxheaderparser/simple.py @@ -236,6 +236,8 @@ class SimpleCxxVisitor: parent_ns.namespaces[name] = ns parent_ns = ns + assert ns is not None + self.block = ns self.namespace = ns @@ -247,15 +249,18 @@ class SimpleCxxVisitor: self.block.forward_decls.append(fdecl) def on_variable(self, state: State, v: Variable) -> None: + assert isinstance(self.block, NamespaceScope) self.block.variables.append(v) def on_function(self, state: State, fn: Function) -> None: + assert isinstance(self.block, NamespaceScope) self.block.functions.append(fn) def on_typedef(self, state: State, typedef: Typedef) -> None: self.block.typedefs.append(typedef) def on_using_namespace(self, state: State, namespace: typing.List[str]) -> None: + assert isinstance(self.block, NamespaceScope) ns = UsingNamespace("::".join(namespace)) self.block.using_ns.append(ns) @@ -283,12 +288,15 @@ class SimpleCxxVisitor: self.block = block def on_class_field(self, state: State, f: Field) -> None: + assert isinstance(self.block, ClassScope) self.block.fields.append(f) def on_class_method(self, state: ClassBlockState, method: Method) -> None: + assert isinstance(self.block, ClassScope) self.block.methods.append(method) def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: + assert isinstance(self.block, ClassScope) self.block.friends.append(friend) def on_class_end(self, state: ClassBlockState) -> None: From ce4124d5dd9c1b563a9d89a8e83de41b881dc631 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:51:19 -0500 Subject: [PATCH 5/8] mypy: add temporary variables to ease type checking --- cxxheaderparser/lexer.py | 8 ++++---- cxxheaderparser/parser.py | 37 ++++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/cxxheaderparser/lexer.py b/cxxheaderparser/lexer.py index 2bfdc08..c160eef 100644 --- a/cxxheaderparser/lexer.py +++ b/cxxheaderparser/lexer.py @@ -274,7 +274,7 @@ class Lexer: self.filenames: typing.List[str] = [] self._filenames_set: typing.Set[str] = set() - if self.filename: + if filename: self.filenames.append(filename) self._filenames_set.add(filename) @@ -334,10 +334,10 @@ class Lexer: del self.comments[:] - comments = "\n".join(comments) + comment_str = "\n".join(comments) del self.comments[:] - if comments: - return comments + if comment_str: + return comment_str return None diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index 0e9548c..ec38e92 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -220,6 +220,8 @@ class CxxParser: match_stack = deque((token_map[tok.type] for tok in consumed)) get_token = self.lex.token + tok: typing.Optional[LexToken] + while True: tok = get_token() consumed.append(tok) @@ -710,10 +712,12 @@ class CxxParser: break # multiple attributes can be specified - tok = self.lex.token_if(*self._attribute_specifier_seq_start_types) - if tok is None: + maybe_tok = self.lex.token_if(*self._attribute_specifier_seq_start_types) + if maybe_tok is None: break + tok = maybe_tok + # TODO return attrs # @@ -1202,10 +1206,13 @@ class CxxParser: props.update(dict.fromkeys(mods.vars.keys(), True)) if is_class_block: + access = self._current_access + assert access is not None + f = Field( name=name, type=dtype, - access=self._current_access, + access=access, value=default, bits=bits, doxygen=doxygen, @@ -1713,6 +1720,9 @@ class CxxParser: method: Method + current_access = self._current_access + assert current_access is not None + if op: method = Operator( return_type, @@ -1722,8 +1732,8 @@ class CxxParser: doxygen=doxygen, operator=op, template=template, - access=self._current_access, - **props, + access=current_access, + **props, # type: ignore ) else: method = Method( @@ -1735,8 +1745,8 @@ class CxxParser: constructor=constructor, destructor=destructor, template=template, - access=self._current_access, - **props, + access=current_access, + **props, # type: ignore ) self._parse_method_end(method) @@ -1772,6 +1782,10 @@ class CxxParser: raise CxxParseError( "typedef name may not be a nested-name-specifier" ) + name: typing.Optional[str] = getattr(pqname.segments[0], "name", None) + if not name: + raise CxxParseError("typedef function must have a name") + if fn.constexpr: raise CxxParseError("typedef function may not be constexpr") if fn.extern: @@ -1798,7 +1812,7 @@ class CxxParser: msvc_convention=fn.msvc_convention, ) - typedef = Typedef(fntype, pqname.segments[0].name, self._current_access) + typedef = Typedef(fntype, name, self._current_access) self.visitor.on_typedef(state, typedef) return False else: @@ -1861,9 +1875,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 + msvc_convention = None + msvc_convention_tok = self.lex.token_if_val(*self._msvc_conventions) + if msvc_convention_tok: + msvc_convention = msvc_convention_tok.value # Check to see if this is a grouping paren or something else if not self.lex.token_peek_if("*", "&"): From 83aa34d06e980d83d1080cc7cb78c350f84b566c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:51:49 -0500 Subject: [PATCH 6/8] mypy: functions should be a list of functions --- cxxheaderparser/simple.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cxxheaderparser/simple.py b/cxxheaderparser/simple.py index de9389c..1602654 100644 --- a/cxxheaderparser/simple.py +++ b/cxxheaderparser/simple.py @@ -93,7 +93,7 @@ class NamespaceScope: classes: typing.List["ClassScope"] = field(default_factory=list) enums: typing.List[EnumDecl] = field(default_factory=list) - functions: typing.List[Method] = field(default_factory=list) + functions: typing.List[Function] = field(default_factory=list) typedefs: typing.List[Typedef] = field(default_factory=list) variables: typing.List[Variable] = field(default_factory=list) From f29c1d5a30e30a4a4668f619a7cdbd29bebd8acf Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:52:36 -0500 Subject: [PATCH 7/8] mypy: address issues parsing const/volatile/*/& items --- cxxheaderparser/parser.py | 47 ++++++++++++++++++++++++++++++++------- cxxheaderparser/types.py | 6 ++--- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index ec38e92..31b51ff 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -602,7 +602,7 @@ class CxxParser: raise self._parse_error(None) mods.validate(var_ok=False, meth_ok=False, msg="") - dtype = self._parse_cv_ptr(parsed_type, nonptr_fn=True) + dtype = self._parse_cv_ptr_or_fn(parsed_type, nonptr_fn=True) self._next_token_must_be(PhonyEnding.type) except CxxParseError: dtype = None @@ -1220,6 +1220,7 @@ class CxxParser: ) self.visitor.on_class_field(class_state, f) else: + assert pqname is not None v = Variable( pqname, dtype, default, doxygen=doxygen, template=template, **props ) @@ -1757,7 +1758,7 @@ class CxxParser: else: # method must not have multiple segments except for operator if len(pqname.segments) > 1: - if not pqname.segments[0].name == "operator": + if getattr(pqname.segments[0], "name", None) != "operator": raise self._parse_error(None) self.visitor.on_class_method(state, method) @@ -1827,6 +1828,9 @@ class CxxParser: assert tok.type == "[" + if isinstance(dtype, (Reference, MoveReference)): + raise CxxParseError("arrays of references are illegal", tok) + toks = self._consume_balanced_tokens(tok) otok = self.lex.token_if("[") if otok: @@ -1843,9 +1847,20 @@ class CxxParser: def _parse_cv_ptr( self, - dtype: typing.Union[Array, FunctionType, Pointer, Type], - nonptr_fn: bool = False, + dtype: DecoratedType, ) -> DecoratedType: + dtype_or_fn = self._parse_cv_ptr_or_fn(dtype) + if isinstance(dtype_or_fn, FunctionType): + raise CxxParseError("unexpected function type") + return dtype_or_fn + + def _parse_cv_ptr_or_fn( + self, + dtype: typing.Union[ + Array, Pointer, MoveReference, Reference, Type, FunctionType + ], + nonptr_fn: bool = False, + ) -> typing.Union[Array, Pointer, MoveReference, Reference, Type, FunctionType]: # nonptr_fn is for parsing function types directly in template specialization while True: @@ -1854,10 +1869,16 @@ class CxxParser: break if tok.type == "*": + if isinstance(dtype, (Reference, MoveReference)): + raise self._parse_error(tok) dtype = Pointer(dtype) elif tok.type == "const": + if not isinstance(dtype, (Pointer, Type)): + raise self._parse_error(tok) dtype.const = True elif tok.type == "volatile": + if not isinstance(dtype, (Pointer, Type)): + raise self._parse_error(tok) dtype.volatile = True elif nonptr_fn: # remove any inner grouping parens @@ -1870,9 +1891,11 @@ class CxxParser: self.lex.return_tokens(toks[1:-1]) fn_params, vararg = self._parse_parameters() - dtype = FunctionType(dtype, fn_params, vararg) + + assert not isinstance(dtype, FunctionType) + dtype = dtype_fn = FunctionType(dtype, fn_params, vararg) if self.lex.token_if("ARROW"): - self._parse_trailing_return_type(dtype) + self._parse_trailing_return_type(dtype_fn) else: msvc_convention = None @@ -1892,11 +1915,14 @@ class CxxParser: aptok = self.lex.token_if("[", "(") if aptok: if aptok.type == "[": + assert not isinstance(dtype, FunctionType) dtype = self._parse_array_type(aptok, dtype) elif aptok.type == "(": fn_params, vararg = self._parse_parameters() # the type we already have is the return type of the function pointer + assert not isinstance(dtype, FunctionType) + dtype = FunctionType( dtype, fn_params, vararg, msvc_convention=msvc_convention ) @@ -1909,11 +1935,13 @@ class CxxParser: # -> this could return some weird results for invalid code, but # we don't support that anyways so it's fine? self.lex.return_tokens(toks[1:-1]) - dtype = self._parse_cv_ptr(dtype) + dtype = self._parse_cv_ptr_or_fn(dtype, nonptr_fn) break tok = self.lex.token_if("&", "DBL_AMP") if tok: + assert not isinstance(dtype, (Reference, MoveReference)) + if tok.type == "&": dtype = Reference(dtype) else: @@ -1922,7 +1950,7 @@ class CxxParser: # peek at the next token and see if it's a paren. If so, it might # be a nasty function pointer if self.lex.token_peek_if("("): - dtype = self._parse_cv_ptr(dtype, nonptr_fn) + dtype = self._parse_cv_ptr_or_fn(dtype, nonptr_fn) return dtype @@ -2147,6 +2175,9 @@ class CxxParser: if op: raise self._parse_error(None) + if not dtype: + raise CxxParseError("appear to be parsing a field without a type") + self._parse_field(mods, dtype, pqname, template, doxygen, location, is_typedef) return False diff --git a/cxxheaderparser/types.py b/cxxheaderparser/types.py index 7973b69..2b6bdb4 100644 --- a/cxxheaderparser/types.py +++ b/cxxheaderparser/types.py @@ -193,7 +193,7 @@ class TemplateArgument: #: If this argument is a type, it is stored here as a DecoratedType, #: otherwise it's stored as an unparsed set of values - arg: typing.Union["DecoratedType", Value] + arg: typing.Union["DecoratedType", "FunctionType", Value] param_pack: bool = False @@ -297,7 +297,7 @@ class Reference: A lvalue (``&``) reference """ - ref_to: typing.Union[Array, Pointer, Type] + ref_to: typing.Union[Array, FunctionType, Pointer, Type] @dataclass @@ -306,7 +306,7 @@ class MoveReference: An rvalue (``&&``) reference """ - moveref_to: typing.Union[Array, Pointer, Type] + moveref_to: typing.Union[Array, FunctionType, Pointer, Type] #: A type or function type that is decorated with various things From 977fe133d00ff2d37623569f569368d6ac95075a Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Jan 2022 21:53:22 -0500 Subject: [PATCH 8/8] Check codebase using mypy in github actions --- .github/workflows/dist.yml | 18 +++++++++++++++++- cxxheaderparser/py.typed | 0 mypy.ini | 5 +++++ setup.py | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 cxxheaderparser/py.typed create mode 100644 mypy.ini diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 68bdd86..963de6c 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -16,6 +16,22 @@ jobs: - uses: actions/checkout@v2 - uses: psf/black@stable + check-mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + # - uses: jpetrucciani/mypy-check@0.930 + # .. can't use that because we need to install pytest + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install requirements + run: | + pip --disable-pip-version-check install mypy pytest + - name: Run mypy + run: | + mypy . + check-doc: runs-on: ubuntu-18.04 @@ -77,7 +93,7 @@ jobs: publish: runs-on: ubuntu-latest - needs: [check, check-doc, test] + needs: [check, check-mypy, check-doc, test] if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') steps: diff --git a/cxxheaderparser/py.typed b/cxxheaderparser/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..6a3cd39 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,5 @@ +[mypy] +exclude = setup\.py|docs + +[mypy-cxxheaderparser._ply.*] +ignore_errors = True \ No newline at end of file diff --git a/setup.py b/setup.py index 241ca7d..288746a 100644 --- a/setup.py +++ b/setup.py @@ -71,6 +71,7 @@ setup( license="BSD", platforms="Platform Independent", packages=find_packages(), + package_data={"cxxheaderparser": ["py.typed"]}, keywords="c++ header parser ply", python_requires=">= 3.6", classifiers=CLASSIFIERS,