├── test ├── empty.cc ├── external │ ├── me.h │ └── foo.h ├── invalid1.h ├── asm.h ├── macro1.h ├── bar.h ├── cxx.cxx ├── invalid2.h ├── itself.h ├── src │ └── foo.cpp ├── bar_bis.h ├── foo.hxx ├── ignore │ └── ignore.cc ├── include.cc ├── macro2.h ├── myenum.h ├── something1.h ├── syntax.cc ├── undefined.cc ├── macro6.h ├── no_eol.cc ├── not-used.h ├── test.cc ├── macro11.h ├── define │ ├── other_defined1.h │ ├── d3.h │ ├── d3.cc │ ├── d6.h │ ├── d4-used.h │ ├── d4.h │ ├── d7.h │ ├── d9.h │ ├── d5.h │ ├── d8.h │ ├── d1.h │ ├── d2.cc │ └── d1.cc ├── template3.h ├── exported.h ├── latin1.cc ├── template4.h ├── using_parent.h ├── declspec.h ├── macro5.h ├── struct.h ├── typename.h ├── constexpr.h ├── template7.h ├── using_as_typedef.h ├── macro3.h ├── dup.h ├── xss_module.h ├── include.h ├── std_function.h ├── invalidchar.h ├── template2.h ├── const_define.h ├── second.h ├── baz.h ├── macro14.h ├── using_child.h ├── macro13.h ├── first.h ├── template.h ├── macro9.h ├── inline.h ├── macro7.h ├── anonymous_struct.h ├── macro10.h ├── need-class.h ├── function_pointers.h ├── template6.h ├── test.h ├── macro15.h ├── constexpr.cc ├── undefined.h ├── template5.h ├── template_specialization.h ├── noexcept.h ├── with_main.cc ├── incomplete_type.h ├── typedef.h ├── typedef2.h ├── union.h ├── enum.h ├── infinite_loop.h ├── macro12.h ├── using.h ├── qt.h ├── specifiers.h ├── ctor_init_list.h ├── macro4.h ├── init_lists.h ├── unused_static.cc ├── c++11.h ├── macro16.h ├── template8.h ├── attribute.h ├── macro8.h ├── lookup.h ├── expected.txt ├── .ignore.h └── foo.h ├── cpp ├── __init__.py ├── metrics.py ├── headers.py ├── utils.py ├── nonvirtual_dtors.py ├── keywords.py ├── static_data.py ├── symbols.py ├── tokenize.py └── find_warnings.py ├── MANIFEST.in ├── .gitignore ├── .travis.yml ├── test.bash ├── test_find_warnings.py ├── setup.py ├── Makefile ├── README.rst ├── cppclean ├── test_symbols.py ├── COPYING ├── test_tokenize.py └── test_ast.py /test/empty.cc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/external/me.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/invalid1.h: -------------------------------------------------------------------------------- 1 | @foo 2 | -------------------------------------------------------------------------------- /test/asm.h: -------------------------------------------------------------------------------- 1 | asm("int $21"); 2 | -------------------------------------------------------------------------------- /test/macro1.h: -------------------------------------------------------------------------------- 1 | MyMacro(Get) 2 | -------------------------------------------------------------------------------- /test/bar.h: -------------------------------------------------------------------------------- 1 | 2 | class Bar { 3 | }; 4 | -------------------------------------------------------------------------------- /test/cxx.cxx: -------------------------------------------------------------------------------- 1 | int cxx() 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /test/external/foo.h: -------------------------------------------------------------------------------- 1 | class Unused; 2 | -------------------------------------------------------------------------------- /test/invalid2.h: -------------------------------------------------------------------------------- 1 | #include "etet 2 | -------------------------------------------------------------------------------- /test/itself.h: -------------------------------------------------------------------------------- 1 | #include "itself.h" 2 | -------------------------------------------------------------------------------- /test/src/foo.cpp: -------------------------------------------------------------------------------- 1 | #include "foo.h" 2 | -------------------------------------------------------------------------------- /cpp/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.13' 2 | -------------------------------------------------------------------------------- /test/bar_bis.h: -------------------------------------------------------------------------------- 1 | 2 | class Bar2 { 3 | }; 4 | -------------------------------------------------------------------------------- /test/foo.hxx: -------------------------------------------------------------------------------- 1 | class Impl 2 | { 3 | }; 4 | -------------------------------------------------------------------------------- /test/ignore/ignore.cc: -------------------------------------------------------------------------------- 1 | static int x = 3; 2 | -------------------------------------------------------------------------------- /test/include.cc: -------------------------------------------------------------------------------- 1 | #include "empty.cc" 2 | -------------------------------------------------------------------------------- /test/macro2.h: -------------------------------------------------------------------------------- 1 | MyMacro(Get) 2 | MyMacro(Set) 3 | -------------------------------------------------------------------------------- /test/myenum.h: -------------------------------------------------------------------------------- 1 | enum MyEnum {FOO, BAR, BAZ}; 2 | -------------------------------------------------------------------------------- /test/something1.h: -------------------------------------------------------------------------------- 1 | 2 | class Something {}; 3 | -------------------------------------------------------------------------------- /test/syntax.cc: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /test/undefined.cc: -------------------------------------------------------------------------------- 1 | #include "undefined.h" 2 | -------------------------------------------------------------------------------- /test/macro6.h: -------------------------------------------------------------------------------- 1 | class Foo { 2 | MY_MACRO; 3 | }; 4 | -------------------------------------------------------------------------------- /test/no_eol.cc: -------------------------------------------------------------------------------- 1 | #ifdef 1 2 | int main() {} 3 | #endif -------------------------------------------------------------------------------- /test/not-used.h: -------------------------------------------------------------------------------- 1 | 2 | class PleaseDontUseMe_KThxBye {}; 3 | -------------------------------------------------------------------------------- /test/test.cc: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "bar.h" 3 | -------------------------------------------------------------------------------- /test/macro11.h: -------------------------------------------------------------------------------- 1 | class Foo : public MyMacro(Bar) 2 | { 3 | }; 4 | -------------------------------------------------------------------------------- /test/define/other_defined1.h: -------------------------------------------------------------------------------- 1 | 2 | void SomeOtherFunction1(void); 3 | -------------------------------------------------------------------------------- /test/template3.h: -------------------------------------------------------------------------------- 1 | template 2 | class Foo* fct(T t); 3 | -------------------------------------------------------------------------------- /test/exported.h: -------------------------------------------------------------------------------- 1 | class DLLEXPORT Foo{}; 2 | struct DLLEXPORT Bar{}; 3 | -------------------------------------------------------------------------------- /test/define/d3.h: -------------------------------------------------------------------------------- 1 | 2 | namespace Namespace { 3 | void Function(); 4 | } 5 | -------------------------------------------------------------------------------- /test/latin1.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cppclean/HEAD/test/latin1.cc -------------------------------------------------------------------------------- /test/template4.h: -------------------------------------------------------------------------------- 1 | class A; 2 | 3 | template 4 | ::A* B::fn(); 5 | -------------------------------------------------------------------------------- /test/using_parent.h: -------------------------------------------------------------------------------- 1 | namespace parent 2 | { 3 | class Parent {}; 4 | } 5 | -------------------------------------------------------------------------------- /test/declspec.h: -------------------------------------------------------------------------------- 1 | __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; 2 | -------------------------------------------------------------------------------- /test/define/d3.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "d3.h" 3 | 4 | void Namespace::Function() {} 5 | -------------------------------------------------------------------------------- /test/macro5.h: -------------------------------------------------------------------------------- 1 | #define MY_MACRO {\ 2 | /* comment */\ 3 | OTHER_MACRO;\ 4 | } 5 | -------------------------------------------------------------------------------- /test/define/d6.h: -------------------------------------------------------------------------------- 1 | 2 | class Foo; 3 | 4 | class Bar { 5 | friend class Foo; 6 | }; 7 | -------------------------------------------------------------------------------- /test/define/d4-used.h: -------------------------------------------------------------------------------- 1 | 2 | class Outer { 3 | public: 4 | class Inner { 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /test/struct.h: -------------------------------------------------------------------------------- 1 | struct ssl_session_st *prev, *next; 2 | 3 | struct Init { 4 | } static init; 5 | -------------------------------------------------------------------------------- /test/typename.h: -------------------------------------------------------------------------------- 1 | class Foo { 2 | template 3 | friend typename T::Type* fn(); 4 | }; 5 | -------------------------------------------------------------------------------- /test/define/d4.h: -------------------------------------------------------------------------------- 1 | 2 | #include "d4-used.h" 3 | 4 | class MySpiffyClass : public Outer::Inner { 5 | }; 6 | -------------------------------------------------------------------------------- /test/constexpr.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_CONSTEXPR_H 2 | #define TEST_CONSTEXPR_H 3 | 4 | int test(); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /test/template7.h: -------------------------------------------------------------------------------- 1 | template 2 | class Utf; 3 | 4 | template <> 5 | class Utf<8> 6 | { 7 | }; 8 | -------------------------------------------------------------------------------- /test/using_as_typedef.h: -------------------------------------------------------------------------------- 1 | class Foo; 2 | using FooPtr = std::shared_ptr; 3 | void bar(FooPtr foo_ptr); 4 | -------------------------------------------------------------------------------- /test/macro3.h: -------------------------------------------------------------------------------- 1 | MyMacro(NS, Name) 2 | 3 | namespace NS { 4 | MyMacro() 5 | MyMacro(NS) 6 | MyMacro(NS, Name) 7 | } 8 | -------------------------------------------------------------------------------- /test/dup.h: -------------------------------------------------------------------------------- 1 | #include "bar.h" 2 | 3 | class Bar; 4 | 5 | class Foo 6 | { 7 | Bar b; 8 | Bar* bb; 9 | }; 10 | -------------------------------------------------------------------------------- /test/xss_module.h: -------------------------------------------------------------------------------- 1 | 2 | namespace XSS { 3 | class Module { 4 | public: 5 | Module(const char* name) {} 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /test/define/d7.h: -------------------------------------------------------------------------------- 1 | 2 | namespace ns { 3 | class Foo; 4 | class Bar { 5 | virtual void Func(const Foo& foo); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /test/include.h: -------------------------------------------------------------------------------- 1 | #include "empty.cc" 2 | #include "foo.h" 3 | #include "include.h" 4 | #include "foo.h" 5 | #include MY_MACRO 6 | -------------------------------------------------------------------------------- /test/std_function.h: -------------------------------------------------------------------------------- 1 | template 2 | std::function 3 | foo() { 4 | return [&](T a) {return a.method();}; 5 | } 6 | -------------------------------------------------------------------------------- /test/define/d9.h: -------------------------------------------------------------------------------- 1 | namespace ns { 2 | class Foo; 3 | namespace nss { 4 | class Bar { 5 | void Func(const Foo& foo); 6 | }; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/invalidchar.h: -------------------------------------------------------------------------------- 1 | // This compiles correctly, and should not hang the tokenizer in an infinite loop 2 | #if 0 3 | testé 4 | #endif -------------------------------------------------------------------------------- /test/template2.h: -------------------------------------------------------------------------------- 1 | template <> 2 | class ErrorOr { 3 | 4 | ~ErrorOr() { 5 | getPointer()->release(); 6 | } 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /test/const_define.h: -------------------------------------------------------------------------------- 1 | #ifndef CONST_DEFINE_H 2 | # define CONST_DEFINE_H 3 | 4 | # define CONST_DEFINE 1 5 | 6 | #endif //! CONST_DEFINE_H 7 | -------------------------------------------------------------------------------- /test/second.h: -------------------------------------------------------------------------------- 1 | namespace a 2 | { 3 | namespace b 4 | { 5 | namespace c 6 | { 7 | class Second 8 | { 9 | }; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/baz.h: -------------------------------------------------------------------------------- 1 | #include "bar.h" 2 | #include "bar_bis.h" 3 | #include "typedef.h" 4 | 5 | void fct(Bar& bar); 6 | void fct(Bar2&); 7 | void fct(Type&); 8 | -------------------------------------------------------------------------------- /test/macro14.h: -------------------------------------------------------------------------------- 1 | class Foo : 2 | #if defined(WCHAR_MIN) && defined(WCHAR_MAX) 3 | public Bar 4 | #else 5 | #error ERROR 6 | #endif 7 | { 8 | }; 9 | -------------------------------------------------------------------------------- /test/using_child.h: -------------------------------------------------------------------------------- 1 | #include "using_parent.h" 2 | 3 | using namespace parent; 4 | 5 | namespace child 6 | { 7 | class Child : Parent {}; 8 | } 9 | -------------------------------------------------------------------------------- /test/macro13.h: -------------------------------------------------------------------------------- 1 | class bad_any_cast : 2 | #ifndef BOOST_NO_RTTI 3 | public std::bad_cast 4 | #else 5 | public std::exception 6 | #endif 7 | { 8 | }; 9 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | include MANIFEST.in 3 | include README.rst 4 | 5 | exclude .travis.yml 6 | exclude Makefile 7 | exclude test* 8 | prune test 9 | -------------------------------------------------------------------------------- /test/first.h: -------------------------------------------------------------------------------- 1 | #include "second.h" 2 | 3 | namespace a 4 | { 5 | namespace b 6 | { 7 | void fn() 8 | { 9 | using c::Second; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/template.h: -------------------------------------------------------------------------------- 1 | template 2 | class Foo 3 | { 4 | }; 5 | 6 | 7 | Foo bar() 8 | { 9 | Foo x; 10 | return x; 11 | } 12 | -------------------------------------------------------------------------------- /test/define/d5.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | class Foo; 5 | 6 | void Func(vector foos); 7 | 8 | class Bar; 9 | vector Func2(); 10 | -------------------------------------------------------------------------------- /test/macro9.h: -------------------------------------------------------------------------------- 1 | #if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570)) 2 | BOOST_STATIC_ASSERT(I < (int)N); 3 | BOOST_STATIC_ASSERT(I >= 0); 4 | #endif 5 | -------------------------------------------------------------------------------- /test/inline.h: -------------------------------------------------------------------------------- 1 | class SharedCount { 2 | friend inline bool operator==(SharedCount const & a, SharedCount const & b) 3 | { 4 | return a._pi == b._pi; 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /test/define/d8.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | namespace ns { 5 | class OneReg; 6 | class Foo { 7 | auto_ptr saved_; 8 | }; 9 | } // namespace ns 10 | -------------------------------------------------------------------------------- /test/macro7.h: -------------------------------------------------------------------------------- 1 | struct VariadicFunction { 2 | LLVM_DEFINE_OVERLOAD(1) 3 | #undef LLVM_DEFINE_OVERLOAD 4 | }; 5 | 6 | #define MY_THROW() throw () 7 | void Foo() MY_THROW() {} 8 | -------------------------------------------------------------------------------- /test/anonymous_struct.h: -------------------------------------------------------------------------------- 1 | union test 2 | { 3 | unsigned int data; 4 | struct 5 | { 6 | unsigned int first; 7 | unsigned int second; 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tmp 2 | .*.swp 3 | *.pyc 4 | MANIFEST 5 | README.html 6 | __pycache__/ 7 | .tox/ 8 | build/ 9 | dist/ 10 | htmlcov/ 11 | *.egg*/ 12 | .coverage 13 | .travis-solo/ 14 | tags 15 | -------------------------------------------------------------------------------- /test/macro10.h: -------------------------------------------------------------------------------- 1 | STACK_OF(X509_NAME) *ca_names; 2 | STACK_OF(SSL_CIPHER) **skp; 3 | STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *s); 4 | STACK_OF(SSL_CIPHER) **ssl_get_ciphers_by_id(SSL *s); 5 | -------------------------------------------------------------------------------- /test/need-class.h: -------------------------------------------------------------------------------- 1 | 2 | #include "something1.h" 3 | 4 | class Bar; 5 | class Baz; 6 | class Bong; 7 | class Bling; 8 | 9 | class F : public Bar, Bong, Something { 10 | }; 11 | -------------------------------------------------------------------------------- /test/function_pointers.h: -------------------------------------------------------------------------------- 1 | template 2 | inline int (*get_array_pod_sort_comparator(const T &)) 3 | (const void*, const void*) { 4 | return array_pod_sort_comparator; 5 | } 6 | -------------------------------------------------------------------------------- /test/template6.h: -------------------------------------------------------------------------------- 1 | class CMyClass 2 | { 3 | public: 4 | CMyClass(); 5 | 6 | private: 7 | std::function mFunctor; 8 | static const bool b1 = sizeof(int) > 4; 9 | }; 10 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #include "bar.h" 2 | #include "me.h" 3 | #include "myenum.h" 4 | 5 | class Test 6 | { 7 | Test(const Bar& bar); 8 | const Bar bar_; 9 | 10 | void f(MyEnum & bar); 11 | }; 12 | -------------------------------------------------------------------------------- /test/macro15.h: -------------------------------------------------------------------------------- 1 | #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED 2 | template 3 | #endif 4 | class flat_set 5 | { 6 | BOOST_COPYABLE_AND_MOVABLE(flat_set) 7 | private: 8 | }; 9 | -------------------------------------------------------------------------------- /test/constexpr.cc: -------------------------------------------------------------------------------- 1 | #include "constexpr.h" 2 | 3 | 4 | constexpr auto x = 1; 5 | 6 | constexpr auto foo() 7 | { 8 | return 2; 9 | } 10 | 11 | 12 | int test() 13 | { 14 | return x + foo(); 15 | } 16 | -------------------------------------------------------------------------------- /test/undefined.h: -------------------------------------------------------------------------------- 1 | namespace undefined 2 | { 3 | void function(); 4 | } 5 | 6 | template 7 | inline void f(T value) 8 | { 9 | } 10 | 11 | template <> 12 | inline void f(int value) 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /test/define/d1.h: -------------------------------------------------------------------------------- 1 | 2 | void SomePublicFunction(void); 3 | 4 | void OnlyDeclared(void); 5 | 6 | class SomeClass { 7 | void Method(); 8 | }; 9 | 10 | template 11 | void template_is_okay() 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /test/template5.h: -------------------------------------------------------------------------------- 1 | const std::vector> v; 2 | 3 | class RegionNode : public RegionNodeBase> { 4 | }; 5 | 6 | template 7 | struct FoldingSetTrait> { 8 | }; 9 | -------------------------------------------------------------------------------- /test/define/d2.cc: -------------------------------------------------------------------------------- 1 | 2 | void SomeFunction() {} 3 | 4 | static void static_functions_are_fine() 5 | { 6 | } 7 | 8 | template 9 | static void static_templated_functions_are_fine() 10 | { 11 | T t; 12 | t.foo(); 13 | } 14 | -------------------------------------------------------------------------------- /test/template_specialization.h: -------------------------------------------------------------------------------- 1 | class A; 2 | 3 | template <> 4 | template <> 5 | A* Foo<0>::fn<1>(); 6 | 7 | class B; 8 | 9 | template<> 10 | void foo(); 11 | 12 | class C; 13 | class D; 14 | 15 | template<> 16 | void foo>(); 17 | -------------------------------------------------------------------------------- /test/noexcept.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_NOEXCEPT_H 2 | #define TEST_NOEXCEPT_H 3 | 4 | inline int no_except_test() noexcept 5 | { 6 | return 0; 7 | }; 8 | 9 | inline int no_except_test() noexcept(false) 10 | { 11 | return 0; 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /test/with_main.cc: -------------------------------------------------------------------------------- 1 | #include "include.h" 2 | #include "include.h" 3 | #include "include.h" 4 | 5 | using namespace std; 6 | 7 | #define A_DEFINE 1 8 | 9 | class Foo; 10 | class Bar; 11 | 12 | FOO() 13 | { 14 | } 15 | 16 | int main() 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /test/incomplete_type.h: -------------------------------------------------------------------------------- 1 | class Foo; 2 | void fn(Foo f); 3 | void fn(Foo* f = 0); 4 | void fn(Foo* f = NULL); 5 | void fn(Foo* f = nullptr); 6 | 7 | #include "bar.h" 8 | void fn(Bar* b = new Bar); 9 | 10 | #include "bar_bis.h" 11 | void fn(std::vector f){42} 12 | -------------------------------------------------------------------------------- /test/typedef.h: -------------------------------------------------------------------------------- 1 | class Foo; 2 | class Bar; 3 | struct Baz; 4 | typedef Plain p; 5 | typedef Ref r; 6 | typedef Baz* b; 7 | typedef int Type; 8 | typedef int& reference; 9 | typedef int (*pf1)(int, int); 10 | typedef int (&pf2)(int, int); 11 | typedef int pf3(); 12 | -------------------------------------------------------------------------------- /test/typedef2.h: -------------------------------------------------------------------------------- 1 | class Bar; 2 | class Barr; 3 | class Baz; 4 | 5 | namespace A { 6 | class B; 7 | class C; 8 | } 9 | 10 | class Foo { 11 | friend class A::B; 12 | friend A::C; 13 | friend class Bar; 14 | friend Barr; 15 | friend void fn(Baz* baz); 16 | }; 17 | -------------------------------------------------------------------------------- /test/union.h: -------------------------------------------------------------------------------- 1 | template 2 | union SizerImpl { 3 | char arr1[sizeof(T)]; 4 | }; 5 | 6 | class Foo; 7 | 8 | union U { 9 | Foo* foo; 10 | }; 11 | 12 | template 13 | union offset_ptr_internal 14 | { 15 | offset_ptr_internal(OffsetType off) 16 | {} 17 | }; 18 | -------------------------------------------------------------------------------- /test/enum.h: -------------------------------------------------------------------------------- 1 | enum {}; 2 | enum : int {}; 3 | enum class {}; 4 | enum class : int {}; 5 | enum Color {}; 6 | enum Color : int {}; 7 | enum class Color {}; 8 | enum class Color : int {}; 9 | 10 | enum Color : unsigned int; 11 | enum class Color; 12 | enum class Color : unsigned int; 13 | 14 | enum Color color; 15 | -------------------------------------------------------------------------------- /test/infinite_loop.h: -------------------------------------------------------------------------------- 1 | template 2 | #if defined(GENERATING_DOCUMENTATION) 3 | unspecified 4 | #else 5 | inline detail::wrapped_handler 6 | #endif 7 | io_service::wrap(Handler handler) 8 | { 9 | return detail::wrapped_handler(*this, handler); 10 | } 11 | -------------------------------------------------------------------------------- /test/macro12.h: -------------------------------------------------------------------------------- 1 | template < 2 | std::size_t size_ 3 | , std::size_t alignment_ = std::size_t(-1) 4 | > 5 | class aligned_storage : 6 | #ifndef __BORLANDC__ 7 | private 8 | #else 9 | public 10 | #endif 11 | ::boost::detail::aligned_storage::aligned_storage_imp 12 | { 13 | }; 14 | -------------------------------------------------------------------------------- /test/using.h: -------------------------------------------------------------------------------- 1 | // Forward declare Foo::Bar 2 | namespace Foo 3 | { 4 | class Bar; 5 | } 6 | 7 | // So we can reference Bar without qualification below. 8 | using namespace Foo; 9 | 10 | class A 11 | { 12 | // The namespaced forward declaration is required for this line of code. 13 | void f(Bar & bar); 14 | }; 15 | -------------------------------------------------------------------------------- /test/qt.h: -------------------------------------------------------------------------------- 1 | class QSomeClass; 2 | 3 | class QSomeOtherClass { 4 | QPointer ptr1; 5 | QSharedDataPointer ptr2; 6 | QSharedPointer ptr3; 7 | QWeakPointer ptr4; 8 | QScopedPointer ptr5; 9 | QScopedArrayPointer ptr6; 10 | }; 11 | -------------------------------------------------------------------------------- /test/specifiers.h: -------------------------------------------------------------------------------- 1 | class Base { 2 | virtual ~Base(); 3 | virtual foo(); 4 | virtual bar(); 5 | }; 6 | 7 | class Derived { 8 | void foo() override { 9 | getKey()->skip(); 10 | getValue()->skip(); 11 | } 12 | 13 | void bar() final { 14 | getKey()->skip(); 15 | getValue()->skip(); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/ctor_init_list.h: -------------------------------------------------------------------------------- 1 | class Foo { 2 | public: 3 | int first, 4 | #if defined(TEST) 5 | int second; 6 | #endif 7 | }; 8 | 9 | Foo::Foo(): 10 | first(1) 11 | #if defined(TEST) // this 3 lines 12 | ,second(2) // generated 13 | #endif // IndexError: list index out of range 14 | {} 15 | -------------------------------------------------------------------------------- /test/macro4.h: -------------------------------------------------------------------------------- 1 | class Foo { 2 | MY_MACRO 3 | }; 4 | 5 | class Bar { 6 | void fct(); 7 | 8 | MY_MACRO 9 | }; 10 | 11 | class Baz { 12 | MY_MACRO(Baz) 13 | }; 14 | 15 | class Qux { 16 | void fct(); 17 | 18 | MY_MACRO(Qux) 19 | }; 20 | 21 | #ifndef MY_MACRO_H 22 | #define MY_MACRO_H 23 | 24 | MY_MACRO(Foo) 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /test/init_lists.h: -------------------------------------------------------------------------------- 1 | #include "const_define.h" 2 | 3 | class A { 4 | public: 5 | A() : 6 | abc(CONST_DEFINE), 7 | efg(2), 8 | hij(3) 9 | {} 10 | private: 11 | int abc; 12 | int efg; 13 | int hij; 14 | }; 15 | 16 | struct B { 17 | int arg1; 18 | 19 | B() : 20 | arg1(CONST_DEFINE) 21 | {} 22 | }; 23 | -------------------------------------------------------------------------------- /test/unused_static.cc: -------------------------------------------------------------------------------- 1 | static int x = 1; 2 | static int y = 1; 3 | static const int z = 1; 4 | static const int a, b; 5 | bool plugged[42]; 6 | int array[2][1]; 7 | int i[1<2]; 8 | int j[1>2]; 9 | 10 | class Foo 11 | { 12 | void fct() 13 | { 14 | x = 2; 15 | } 16 | }; 17 | 18 | int main() 19 | { 20 | y = 3; 21 | a = 0; 22 | } 23 | -------------------------------------------------------------------------------- /test/c++11.h: -------------------------------------------------------------------------------- 1 | class X { 2 | X& operator=(const X&) = delete; 3 | X(const X&) = delete; 4 | }; 5 | 6 | class Y { 7 | Y& operator=(const Y&) = default; 8 | Y(const Y&) = default; 9 | }; 10 | 11 | auto bar(void) -> void; 12 | 13 | const struct { 14 | char c[2]; 15 | } c = {.c={'\1'}}; 16 | 17 | A::A() : B::B({}), m1(v1), m2(v2) {} 18 | 19 | B::B() : v{}, m1(v1), m2(v2) {} 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | - "3.4" 6 | - "3.5" 7 | - "3.6" 8 | - "nightly" 9 | 10 | install: 11 | - python setup.py --quiet install 12 | 13 | script: 14 | - ./test.bash 15 | 16 | after_success: 17 | - pip install --quiet coverage 18 | - make coverage 19 | - pip install --quiet coveralls 20 | - coveralls 21 | -------------------------------------------------------------------------------- /test/macro16.h: -------------------------------------------------------------------------------- 1 | #if defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 2 | template 3 | #else 4 | template 5 | #endif 6 | class any_base_hook 7 | : public make_any_base_hook 8 | #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 9 | 10 | #else 11 | 12 | #endif 13 | ::type 14 | { 15 | }; 16 | -------------------------------------------------------------------------------- /test/define/d1.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "d1.h" 3 | #include "other_defined1.h" 4 | 5 | static void SomeStaticFunction() { } 6 | 7 | namespace { 8 | void SomeAnonymousFunction() { } 9 | } 10 | 11 | void SomeFunction1() { 12 | SomeStaticFunction(); 13 | } 14 | 15 | void SomeOtherFunction1() { 16 | SomeAnonymousFunction(); 17 | } 18 | 19 | void SomePublicFunction() { 20 | } 21 | 22 | void SomeClass::Method() { 23 | } 24 | -------------------------------------------------------------------------------- /test/template8.h: -------------------------------------------------------------------------------- 1 | template struct bind_t_generator 2 | { 3 | template class implementation 4 | { 5 | }; 6 | }; 7 | 8 | template 9 | class bind_t: public bind_t_generator::template implementation 10 | { 11 | }; 12 | 13 | template 14 | class bind_t: public bind_t_generator::BOOST_NESTED_TEMPLATE implementation 15 | { 16 | }; 17 | -------------------------------------------------------------------------------- /test.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | trap "echo -e '\x1b[01;31mFailed\x1b[0m'" ERR 4 | 5 | for test in test_*.py 6 | do 7 | $PYTHON ./"$test" 8 | done 9 | 10 | $PYTHON ./cppclean test/c++11.h 11 | $PYTHON ./cppclean test/init_lists.h 12 | 13 | rm -f '.tmp' 14 | $PYTHON ./cppclean \ 15 | --include-path='test/external' \ 16 | --exclude='ignore.cc' \ 17 | 'test' | tr '\\' '/' 2>&1 > '.tmp' || true 18 | diff --unified 'test/expected.txt' '.tmp' 19 | rm -f '.tmp' 20 | 21 | echo -e '\x1b[01;32mOkay\x1b[0m' 22 | -------------------------------------------------------------------------------- /test/attribute.h: -------------------------------------------------------------------------------- 1 | static buffers_iterator begin(const BufferSequence& buffers) 2 | #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) 3 | __attribute__ ((__noinline__)) 4 | #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) 5 | { 6 | } 7 | 8 | class Args; 9 | 10 | struct sum_kahan_impl 11 | { 12 | void 13 | #if BOOST_ACCUMULATORS_GCC_VERSION > 40305 14 | __attribute__((__optimize__("no-associative-math"))) 15 | #endif 16 | operator ()(Args const & args) 17 | { 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /test/macro8.h: -------------------------------------------------------------------------------- 1 | class AssertingVH 2 | #ifndef NDEBUG 3 | : public ValueHandleBase 4 | #endif 5 | { 6 | }; 7 | 8 | template 9 | struct is_noncopyable 10 | #if !defined(BOOST_BROKEN_IS_BASE_AND_DERIVED) && !defined(BOOST_NO_IS_ABSTRACT) 11 | : boost::mpl::or_< 12 | boost::is_abstract 13 | , boost::is_base_and_derived 14 | > 15 | #elif !defined(BOOST_BROKEN_IS_BASE_AND_DERIVED) 16 | : boost::is_base_and_derived 17 | #elif !defined(BOOST_NO_IS_ABSTRACT) 18 | : boost::is_abstract 19 | #else 20 | : boost::mpl::false_ 21 | #endif 22 | { 23 | }; 24 | -------------------------------------------------------------------------------- /test/lookup.h: -------------------------------------------------------------------------------- 1 | class A; 2 | 3 | namespace ns1 4 | { 5 | typedef ::A a; 6 | } 7 | 8 | 9 | namespace ns1 10 | { 11 | namespace ns2 12 | { 13 | template 14 | class B; 15 | } 16 | } 17 | 18 | namespace ns1 19 | { 20 | typedef ns2::B b; 21 | } 22 | 23 | 24 | class C; 25 | 26 | namespace ns3 27 | { 28 | class C; 29 | 30 | void f1(::C& c); 31 | void f2(C& c); 32 | } 33 | 34 | 35 | namespace ns4 36 | { 37 | namespace 38 | { 39 | namespace ns5 40 | { 41 | class D; 42 | void f(D& d); 43 | } 44 | } 45 | } 46 | 47 | 48 | class E; 49 | 50 | namespace ns6 51 | { 52 | class E 53 | { 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /test_find_warnings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Tests for find_warnings module.""" 4 | 5 | from __future__ import absolute_import 6 | 7 | import unittest 8 | 9 | from cpp import find_warnings 10 | 11 | 12 | class Tests(unittest.TestCase): 13 | 14 | def test_get_correct_include_filename(self): 15 | self.assertEqual( 16 | 'FOO.h', 17 | find_warnings.get_correct_include_filename( 18 | 'foo.h', 19 | ['FOO.h', 'apple.h'])) 20 | 21 | def test_get_correct_include_filename_without_match(self): 22 | self.assertEqual( 23 | None, 24 | find_warnings.get_correct_include_filename( 25 | 'foo.h', 26 | ['f.h', 'apple.h'])) 27 | 28 | self.assertEqual( 29 | None, 30 | find_warnings.get_correct_include_filename( 31 | 'foo.h', 32 | [])) 33 | 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /cpp/metrics.py: -------------------------------------------------------------------------------- 1 | # Copyright 2007 Neal Norwitz 2 | # Portions Copyright 2007 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Calculate metrics for C++ code.""" 17 | 18 | from __future__ import unicode_literals 19 | 20 | 21 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 22 | 23 | 24 | class Metrics(object): 25 | 26 | """Calculate various metrics on C++ source code.""" 27 | 28 | def __init__(self, source): 29 | self.source = source 30 | 31 | def get_line_number(self, index): 32 | """Return the line number in the source based on the index.""" 33 | return 1 + self.source.count('\n', 0, index) 34 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Setup for cppclean.""" 4 | 5 | from __future__ import absolute_import 6 | 7 | import ast 8 | from distutils import core 9 | import os 10 | 11 | 12 | def version(filename): 13 | """Return version string.""" 14 | with open(filename) as input_file: 15 | for line in input_file: 16 | if line.startswith('__version__'): 17 | return ast.parse(line).body[0].value.s 18 | 19 | 20 | with open('README.rst') as readme: 21 | core.setup( 22 | name='cppclean', 23 | version=version(os.path.join('cpp', '__init__.py')), 24 | description='Find problems in C++ source that slow development ' 25 | 'of large code bases.', 26 | long_description=readme.read(), 27 | license='Apache license', 28 | url='https://github.com/myint/cppclean', 29 | classifiers=[ 30 | 'Environment :: Console', 31 | 'Intended Audience :: Developers', 32 | 'Operating System :: OS Independent', 33 | 'Programming Language :: Python', 34 | 'Programming Language :: Python :: 2.7', 35 | 'Programming Language :: Python :: 3', 36 | 'Topic :: Software Development :: Quality Assurance', 37 | ], 38 | packages=['cpp'], 39 | scripts=['cppclean']) 40 | -------------------------------------------------------------------------------- /cpp/headers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2007 Neal Norwitz 2 | # Portions Copyright 2007 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Find and print the headers #include'd in a source file.""" 16 | 17 | from __future__ import absolute_import 18 | from __future__ import print_function 19 | from __future__ import unicode_literals 20 | 21 | import os 22 | 23 | from . import utils 24 | 25 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 26 | 27 | 28 | def read_source(filename, include_paths): 29 | for path in include_paths: 30 | actual_filename = os.path.join(path, filename) 31 | actual_filename = actual_filename if not actual_filename.startswith( 32 | "./") else actual_filename[2:] 33 | source = utils.read_file(actual_filename, False) 34 | if source is not None: 35 | return source, actual_filename 36 | return None, filename 37 | -------------------------------------------------------------------------------- /cpp/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2007 Neal Norwitz 2 | # Portions Copyright 2007 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Generic utilities for C++ parsing.""" 17 | 18 | from __future__ import absolute_import 19 | from __future__ import print_function 20 | from __future__ import unicode_literals 21 | 22 | import io 23 | import sys 24 | 25 | 26 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 27 | 28 | 29 | def read_file(filename, print_error=True): 30 | """Returns the contents of a file.""" 31 | try: 32 | for encoding in ['utf-8', 'latin1']: 33 | try: 34 | with io.open(filename, encoding=encoding) as fp: 35 | return fp.read() 36 | except UnicodeDecodeError: 37 | pass 38 | except IOError as exception: 39 | if print_error: 40 | print(exception, file=sys.stderr) 41 | return None 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: check test 2 | 3 | check: 4 | pylint \ 5 | --reports=no \ 6 | --rcfile=/dev/null \ 7 | --dummy-variables-rgx='^_+$$' \ 8 | --disable=bad-continuation \ 9 | --disable=duplicate-code \ 10 | --disable=fixme \ 11 | --disable=invalid-name \ 12 | --disable=missing-docstring \ 13 | --disable=no-else-return \ 14 | --disable=no-self-use \ 15 | --disable=too-many-arguments \ 16 | --disable=raising-bad-type \ 17 | --disable=redefined-variable-type \ 18 | --disable=simplifiable-if-statement \ 19 | --disable=too-few-public-methods \ 20 | --disable=too-many-locals \ 21 | --disable=too-many-return-statements \ 22 | --disable=too-many-instance-attributes \ 23 | --disable=too-many-public-methods \ 24 | --disable=too-many-branches \ 25 | --disable=too-many-lines \ 26 | --disable=too-many-statements \ 27 | --disable=undefined-loop-variable \ 28 | --disable=unused-argument \ 29 | cpp cppclean setup.py 30 | pycodestyle cpp $(wildcard *.py) 31 | check-manifest 32 | python setup.py --long-description | rstcheck - 33 | 34 | coverage: 35 | @coverage erase 36 | @PYTHON='coverage run --branch --parallel-mode' ./test.bash 37 | @coverage combine 38 | @coverage report 39 | 40 | open_coverage: coverage 41 | @coverage html 42 | @python -m webbrowser -n "file://${PWD}/htmlcov/index.html" 43 | 44 | readme: 45 | @restview --long-description --strict 46 | 47 | test: 48 | ./test.bash 49 | 50 | .PHONY: check coverage open_coverage readme test 51 | -------------------------------------------------------------------------------- /cpp/nonvirtual_dtors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2008 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Print classes which have a virtual method and non-virtual destructor.""" 16 | 17 | from __future__ import print_function 18 | from __future__ import unicode_literals 19 | 20 | from . import ast 21 | from . import metrics 22 | 23 | 24 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 25 | 26 | 27 | def _find_warnings(filename, source, ast_list): 28 | count = 0 29 | for ast_node in ast_list: 30 | if isinstance(ast_node, ast.Class) and ast_node.body: 31 | class_node = ast_node 32 | has_virtuals = False 33 | for node in ast_node.body: 34 | if isinstance(node, ast.Class) and node.body: 35 | _find_warnings(filename, source, [node]) 36 | elif (isinstance(node, ast.Function) and 37 | node.modifiers & ast.FUNCTION_VIRTUAL): 38 | has_virtuals = True 39 | if node.modifiers & ast.FUNCTION_DTOR: 40 | break 41 | else: 42 | if has_virtuals and not class_node.bases: 43 | lines = metrics.Metrics(source) 44 | print( 45 | '%s:%d' % ( 46 | filename, 47 | lines.get_line_number( 48 | class_node.start)), 49 | end=' ') 50 | print("'{}' has virtual methods without a virtual " 51 | 'dtor'.format(class_node.name)) 52 | count += 1 53 | 54 | return count 55 | 56 | 57 | def run(filename, source, entire_ast, include_paths, 58 | system_include_paths, nonsystem_include_paths, quiet): 59 | return _find_warnings(filename, source, entire_ast) 60 | -------------------------------------------------------------------------------- /cpp/keywords.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2007 Neal Norwitz 3 | # Portions Copyright 2007 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """C++ keywords and helper utilities for determining keywords.""" 18 | 19 | from __future__ import unicode_literals 20 | 21 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 22 | 23 | 24 | TYPES = frozenset(['bool', 'char', 'int', 'long', 'short', 'double', 'float', 25 | 'void', 'wchar_t', 'unsigned', 'signed', 'size_t', 'auto', 26 | 'asm']) 27 | 28 | TYPE_MODIFIERS = frozenset(['register', 'const', 'constexpr', 'extern', 29 | 'static', 'volatile', 'mutable']) 30 | 31 | OTHER_MODIFIERS = frozenset(['class', 'struct', 'union', 'enum']) 32 | 33 | ACCESS = frozenset(['public', 'protected', 'private', 'friend']) 34 | 35 | CASTS = frozenset(['static_cast', 'const_cast', 'dynamic_cast', 36 | 'reinterpret_cast']) 37 | 38 | OTHERS = frozenset(['true', 'false', 'asm', 'namespace', 'using', 39 | 'explicit', 'this', 'operator', 'sizeof', 40 | 'new', 'delete', 'typedef', 'typeid', 41 | 'typename', 'template', 'virtual', 'inline']) 42 | 43 | CONTROL = frozenset(['case', 'switch', 'default', 'if', 'else', 'return', 44 | 'goto']) 45 | 46 | EXCEPTION = frozenset(['try', 'catch', 'throw']) 47 | 48 | LOOP = frozenset(['while', 'do', 'for', 'break', 'continue']) 49 | 50 | ALL = (TYPES | TYPE_MODIFIERS | OTHER_MODIFIERS | ACCESS | CASTS | 51 | OTHERS | CONTROL | EXCEPTION | LOOP) 52 | 53 | 54 | def is_keyword(token): 55 | return token in ALL 56 | 57 | 58 | def is_builtin_type(token): 59 | return token in TYPES or token in TYPE_MODIFIERS 60 | 61 | 62 | def is_builtin_modifiers(token): 63 | return token in TYPE_MODIFIERS or token in OTHER_MODIFIERS 64 | 65 | 66 | def is_builtin_other_modifiers(token): 67 | return token in OTHER_MODIFIERS 68 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | cppclean 3 | ======== 4 | 5 | .. image:: https://travis-ci.org/myint/cppclean.svg?branch=master 6 | :target: https://travis-ci.org/myint/cppclean 7 | :alt: Build status 8 | 9 | 10 | Goal 11 | ==== 12 | 13 | cppclean attempts to find problems in C++ source that slow development 14 | in large code bases, for example various forms of unused code. 15 | Unused code can be unused functions, methods, data members, types, etc 16 | to unnecessary #include directives. Unnecessary #includes can cause 17 | considerable extra compiles increasing the edit-compile-run cycle. 18 | 19 | This is a fork of the original cppclean project. The original project home 20 | page, which no longer contains code, is at https://code.google.com/p/cppclean/. 21 | 22 | 23 | Features 24 | ======== 25 | 26 | cppclean finds the following: 27 | 28 | - Classes with virtual methods, no virtual destructor, and no bases 29 | - Global/static data that are potential problems when using threads 30 | - Functions that are declared but not defined 31 | - Unnecessary forward class declarations 32 | - Unnecessary function declarations 33 | - Undeclared function definitions 34 | - Unnecessary #includes in header files 35 | - No direct reference to anything in the header 36 | - Header is unnecessary if classes were forward declared instead 37 | - Inconsistent case in #includes (``foo.h`` versus ``Foo.h``) 38 | - (planned) Unnecessary #includes in source files 39 | - (planned) Source files that reference headers not directly #included, 40 | ie, files that rely on a transitive #include from another header 41 | - (planned) Unused members (private, protected, & public) methods and data 42 | - (planned) ``using namespace std`` in header files 43 | - (planned) Methods that are declared but not defined 44 | 45 | AST is Abstract Syntax Tree, a representation of parsed source code 46 | (https://en.wikipedia.org/wiki/Abstract_syntax_tree). 47 | 48 | 49 | Installation 50 | ============ 51 | 52 | :: 53 | 54 | $ pip install --upgrade cppclean 55 | 56 | 57 | Run 58 | === 59 | 60 | :: 61 | 62 | $ cppclean 63 | 64 | 65 | Multiple include paths can be specified:: 66 | 67 | $ cppclean --include-path=directory1 --include-path=directory2 68 | 69 | 70 | Current status 71 | ============== 72 | 73 | The parser works pretty well for header files, parsing about 99% of Google's 74 | header files. Anything which inspects structure of C++ source files should 75 | work reasonably well. Function bodies are not transformed to an AST, 76 | but left as tokens. 77 | 78 | 79 | Non-goals 80 | ========= 81 | 82 | - Parsing all valid C++ source 83 | - Handling invalid C++ source gracefully 84 | - Compiling to machine code (or anything beyond an AST) 85 | 86 | 87 | Links 88 | ===== 89 | 90 | * Coveralls_ 91 | 92 | .. _`Coveralls`: https://coveralls.io/r/myint/cppclean 93 | -------------------------------------------------------------------------------- /test/expected.txt: -------------------------------------------------------------------------------- 1 | test/baz.h:1: 'bar.h' does not need to be #included; use a forward declaration instead 2 | test/baz.h:2: 'bar_bis.h' does not need to be #included; use a forward declaration instead 3 | test/cxx.cxx:1: 'cxx' not found in any directly #included header 4 | test/declspec.h:1: static data 'NvOptimusEnablement' 5 | test/define/d1.cc:11: 'SomeFunction1' not found in expected header 'test/define/d1.h' or any other directly #included header 6 | test/define/d1.cc:15: expected to find 'SomeOtherFunction1' in 'test/define/d1.h', but found in 'test/define/other_defined1.h' 7 | test/define/d1.h:4: 'OnlyDeclared' declared but not defined 8 | test/define/d2.cc:2: 'SomeFunction' not found in any directly #included header 9 | test/define/d7.h:4 'Bar' has virtual methods without a virtual dtor 10 | test/dup.h:3: 'Bar' forward declared, but already #included in 'test/bar.h' 11 | test/enum.h:14: static data 'color' 12 | test/external/foo.h:1: 'Unused' not used 13 | test/foo.h:13: 'Baz' forward declared, but needs to be #included 14 | test/foo.h:14: 'Unused' not used 15 | test/foo.h:61: 'ProtoArray' not used 16 | test/foo.h:86: 'bar.h' already #included on line 5 17 | test/foo.h:87: 'string' already #included on line 3 18 | test/foo.h:113: 'not-used.h' does not need to be #included 19 | test/foo.h:150: 'AR' not used 20 | test/foo.h:221: unable to find 'dir//bar.h' 21 | test/foo.h:234 'Colon' has virtual methods without a virtual dtor 22 | test/foo.h:20: static data 'd' 23 | test/foo.h:101: static data 'ptof' 24 | test/foo.h:106: static data 'data' 25 | test/foo.h:208: static data 'garbage_state' 26 | test/foo.h:225: static data 'flags_' 27 | test/foo.h:240: static data 'foo' 28 | test/foo.h:242: static data 'GlobalKey' 29 | test/foo.h:269: static data 'Singleton::instance_' 30 | test/foo.h:275: static data 'string_list' 31 | test/include.cc: should #include header file 'test/include.h' 32 | test/include.cc:1: should not #include C++ source file 'empty.cc' 33 | test/include.h:1: 'empty.cc' does not need to be #included 34 | test/include.h:1: should not #include C++ source file 'empty.cc' 35 | test/include.h:3: 'include.h' #includes itself 36 | test/include.h:3: 'include.h' does not need to be #included 37 | test/include.h:4: 'foo.h' already #included on line 2 38 | test/include.h:4: 'foo.h' does not need to be #included 39 | test/itself.h:1: 'itself.h' #includes itself 40 | test/itself.h:1: 'itself.h' does not need to be #included 41 | test/latin1.cc:2: 'latin1' not found in any directly #included header 42 | test/latin1.cc:4: static data 'x' 43 | test/lookup.h:1: 'A' forward declared, but needs to be #included 44 | test/lookup.h:14: 'ns1::ns2::B' forward declared, but needs to be #included 45 | test/lookup.h:48: 'E' not used 46 | test/macro10.h:1: static data 'ca_names' 47 | test/macro10.h:2: static data 'skp' 48 | test/need-class.h:4: 'Bar' forward declared, but needs to be #included 49 | test/need-class.h:5: 'Baz' forward declared, but needs to be #included 50 | test/need-class.h:6: 'Bong' forward declared, but needs to be #included 51 | test/need-class.h:7: 'Bling' forward declared, but needs to be #included 52 | test/struct.h:1: static data 'prev' 53 | test/struct.h:1: static data 'next' 54 | test/struct.h:3: static data 'init' 55 | test/test.cc:2: 'bar.h' already #included in 'test/test.h' 56 | test/test.h:2: 'me.h' does not need to be #included 57 | test/typedef.h:1: 'Foo' forward declared, but needs to be #included 58 | test/undefined.h:3: 'function' declared but not defined 59 | test/unused_static.cc:1: static data 'x' 60 | test/unused_static.cc:2: static data 'y' 61 | test/unused_static.cc:5: static data 'plugged' 62 | test/unused_static.cc:6: static data 'array' 63 | test/unused_static.cc:7: static data 'i' 64 | test/unused_static.cc:8: static data 'j' 65 | test/unused_static.cc:3: unused variable 'z' 66 | test/unused_static.cc:4: unused variable 'b' 67 | test/with_main.cc:2: 'include.h' already #included on line 1 68 | test/with_main.cc:3: 'include.h' already #included on line 1 69 | test/with_main.cc:9: 'Foo' forward declaration not expected in source file 70 | test/with_main.cc:10: 'Bar' forward declaration not expected in source file 71 | -------------------------------------------------------------------------------- /cpp/static_data.py: -------------------------------------------------------------------------------- 1 | # Copyright 2008 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Print classes, functions and modules which contain static data.""" 16 | 17 | from __future__ import print_function 18 | from __future__ import unicode_literals 19 | 20 | import collections 21 | 22 | from . import ast 23 | from . import metrics 24 | 25 | 26 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 27 | 28 | 29 | def _find_warnings(filename, lines, ast_list, static_is_optional): 30 | def print_warning(node): 31 | for name in node.name.split(','): 32 | print("{}:{}: static data '{}'".format( 33 | filename, 34 | lines.get_line_number(node.start), 35 | name)) 36 | 37 | def find_static(function_node): 38 | tokens = [] 39 | static_found = False 40 | for node in function_node.body: 41 | if node.name == 'static': 42 | static_found = True 43 | 44 | if static_found: 45 | tokens.append(node) 46 | if node.name == ';': 47 | body = list( 48 | ast.ASTBuilder(iter(tokens), filename).generate()) 49 | _find_warnings(filename, lines, body, False) 50 | tokens = [] 51 | static_found = False 52 | 53 | count = 0 54 | for node in ast_list: 55 | if isinstance(node, ast.VariableDeclaration): 56 | # Ignore 'static' at module scope so we can find globals too. 57 | is_static = 'static' in node.type.modifiers 58 | is_not_const = ( 59 | 'const' not in node.type.modifiers and 60 | 'constexpr' not in node.type.modifiers 61 | ) 62 | 63 | is_not_none_type = node.name is not None 64 | 65 | if is_not_const and (static_is_optional or is_static) and is_not_none_type: 66 | print_warning(node) 67 | count += 1 68 | elif isinstance(node, ast.Function) and node.body: 69 | find_static(node) 70 | elif isinstance(node, ast.Class) and node.body: 71 | _find_warnings(filename, lines, node.body, False) 72 | 73 | return count 74 | 75 | 76 | def _get_static_declarations(ast_list): 77 | for node in ast_list: 78 | if (isinstance(node, ast.VariableDeclaration) and 79 | 'static' in node.type.modifiers): 80 | for name in node.name.split(','): 81 | yield (name, node) 82 | 83 | 84 | def _find_unused_static_warnings(filename, lines, ast_list): 85 | """Warn about unused static variables.""" 86 | static_declarations = dict(_get_static_declarations(ast_list)) 87 | 88 | def find_variables_use(body): 89 | for child in body: 90 | if child.name in static_declarations: 91 | static_use_counts[child.name] += 1 92 | 93 | static_use_counts = collections.Counter() 94 | for node in ast_list: 95 | if isinstance(node, ast.Function) and node.body: 96 | find_variables_use(node.body) 97 | elif isinstance(node, ast.Class) and node.body: 98 | for child in node.body: 99 | if isinstance(child, ast.Function) and child.body: 100 | find_variables_use(child.body) 101 | 102 | count = 0 103 | for (name, _) in sorted(static_declarations.items(), 104 | key=lambda x: x[1].start): 105 | if not static_use_counts[name]: 106 | print("{}:{}: unused variable '{}'".format( 107 | filename, 108 | lines.get_line_number(static_declarations[name].start), 109 | name)) 110 | count += 1 111 | 112 | return count 113 | 114 | 115 | def run(filename, source, entire_ast, include_paths, 116 | system_include_paths, nonsystem_include_paths, quiet): 117 | lines = metrics.Metrics(source) 118 | 119 | return ( 120 | _find_warnings(filename, lines, entire_ast, True) + 121 | _find_unused_static_warnings(filename, lines, entire_ast) 122 | ) 123 | -------------------------------------------------------------------------------- /test/.ignore.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "bar.h" 6 | #include "xss_module.h" 7 | 8 | 9 | typedef std::map Mapper; 10 | typedef struct { int zz; } AnonStruct; 11 | typedef union NamedUnion { bool xx; } *NamedPtr; 12 | 13 | class Baz; 14 | class Unused; 15 | class Local; 16 | class DataMember; 17 | class Ptr; 18 | 19 | class Data; 20 | extern Data* d; 21 | 22 | namespace somespace { 23 | class Jj; 24 | } 25 | 26 | namespace FooNS { 27 | 28 | class Foo : public Bar { 29 | public: 30 | explicit Foo(); 31 | Foo(register const unsigned long& a, Baz* output) {} 32 | virtual ~Foo() = 0; 33 | 34 | protected: 35 | virtual const somespace::Jj& Get() const; 36 | const somespace::Jj& Get2() const = 0; 37 | int Get3() const { return 0; } 38 | 39 | template 40 | T& foo(T* bar); 41 | 42 | private: 43 | void Method(const Mapper& mapper, somespace::Jj* j); 44 | Mapper& Blah(); 45 | Mapper data_; 46 | DataMember* data; 47 | auto_ptr ptr; 48 | }; 49 | 50 | void Function(const Foo& foo, Bar* bar); 51 | extern const ws::RMM *full; 52 | void Function2(const Local& local); 53 | 54 | } 55 | 56 | class FooModule : public XSS::Module { 57 | public: 58 | FooModule() : XSS::Module("mod") { } 59 | }; 60 | 61 | template class ProtoArray; 62 | 63 | class Experiment { 64 | explicit Experiment(); 65 | 66 | struct Foo { 67 | bool operator() (const Foo *a, const Foo *b) { 68 | return true; 69 | } 70 | }; 71 | 72 | private: 73 | }; 74 | 75 | 76 | namespace noname { 77 | enum nn { 78 | bar, 79 | }; 80 | } 81 | 82 | namespace { 83 | // Anon namespace 84 | } 85 | 86 | #include "bar.h" // foo 87 | #include // foo 88 | 89 | class Abc::Xyz {}; 90 | 91 | class Fool : public Bar, public Baz {}; 92 | 93 | 94 | namespace { 95 | // Anon namespace with something 96 | void lkjsdfkd(); 97 | } 98 | 99 | class TT : public TTbase {}; 100 | 101 | void (*ptof)(const TT& tt); 102 | 103 | typedef void(*Setter)(int*, int); 104 | 105 | // filling data. 106 | Fool data[] = { NULL, }; 107 | 108 | class def_var { 109 | static const int kConstant1 = 5; 110 | static const int kConstant = (kConstant2 - 1) / 4; 111 | }; 112 | 113 | #include "not-used.h" 114 | 115 | namespace { 116 | class LaunchNameEquals { 117 | public: 118 | bool operator()(void* launch) { 119 | return true; 120 | } 121 | }; 122 | } // namespace 123 | 124 | enum {boo}; 125 | 126 | class Blashdfdf { 127 | public: 128 | bool operator()(void* launch) const = 0; 129 | }; 130 | 131 | const char kBS[] = "~!@#$%^&*()-+{}|`[]\\:\";'<>?,./ =\t\r\n\v\f\177"; 132 | 133 | class FooOnYou { 134 | void (sq_type::*field_)(string&); 135 | }; 136 | 137 | namespace wtf = noname; 138 | 139 | void AnnotatedFunction() HOPEFULLY_THIS_IS_AN_ATTR; 140 | void RealAnnotatedFunction() __attribute__((weakref)); 141 | 142 | class TestAttrs { 143 | void AnnotatedMethod() HOPEFULLY_THIS_IS_AN_ATTR; 144 | void RealAnnotatedMethod() __attribute__((weakref)); 145 | }; 146 | 147 | typedef struct _IplImage IplImage; 148 | 149 | namespace { 150 | struct AR; 151 | class BS { }; 152 | } 153 | 154 | class OpCode { 155 | public: 156 | enum Operator { 157 | ID = 0, 158 | CASE = 1, 159 | DEL = 2, 160 | INS = 3, 161 | }; 162 | enum Operator op; 163 | }; 164 | 165 | class MemoryCache { 166 | mutable Mutex index_mutex_; 167 | struct CacheEntry **index_; 168 | }; 169 | 170 | struct lala : dense_hashmap {}; 171 | 172 | #if 0 173 | #if 1 174 | ignore more stuff a second apostrophe here does not work 175 | #else 176 | something else 177 | #endif 178 | #endif 179 | 180 | void Atmak() throw(std::bull); 181 | 182 | enum Status; 183 | enum RPC::Status2; 184 | 185 | typedef class ABC XYZ123; 186 | typedef class CB LP; 187 | typedef class OhMy {} JeeziePeezy; 188 | 189 | template 190 | struct HashIndexSpec { 191 | HashIndexSpec() {} 192 | private: 193 | }; 194 | 195 | template 196 | class ThreadShared::Accessor { 197 | public: 198 | }; 199 | 200 | class FooStr { 201 | char mystr[sizeof(mystr)]; 202 | }; 203 | 204 | class FooStruct { 205 | struct in_addr addr_; 206 | }; 207 | 208 | enum {UNKNOWN = 0, GARBAGE_COLLECTING, NOT_GARBAGE_COLLECTING} garbage_state; 209 | 210 | class InlineClass { 211 | class File* file_; 212 | class File* Method1(); 213 | File* Method2(); 214 | }; 215 | 216 | typedef enum A::D FT; 217 | 218 | #if 0 219 | @ 220 | #endif 221 | #include "dir//bar.h" 222 | 223 | const string single_backslash("\\"); 224 | 225 | struct BitFields { 226 | int done; 227 | } flags_; 228 | 229 | class MyF { 230 | template 231 | friend class shared_ptr; 232 | }; 233 | 234 | class Colon { 235 | virtual ::Foo GetFoo(); 236 | ::Foo GetFoo2(); 237 | }; 238 | 239 | string foo = "\nX=\"/*\""; 240 | 241 | struct streamer::Key GlobalKey; 242 | typedef time_t (* TimeFunc)(time_t*); 243 | typedef char dummy_des_key_schedule[128]; 244 | 245 | class Copy { 246 | public: 247 | Copy(); // normal ctor 248 | Copy& operator=(const Copy& other); // copy ctor 249 | }; 250 | 251 | Copy& Copy::operator=(const Copy& rhs) 252 | { 253 | return *this; 254 | } 255 | 256 | struct Flag; 257 | class StructMethod { 258 | struct Flag *flag(); 259 | }; 260 | 261 | template class TemplateCtorDtor { 262 | public: 263 | TemplateCtorDtor(T *ptr); 264 | ~TemplateCtorDtor(); 265 | }; 266 | 267 | template 268 | Type* Singleton::instance_ = NULL; 269 | 270 | int CreateFakeResults(bool add, float score = 0.5); 271 | 272 | map ReturnFlags(); 273 | 274 | vector string_list; 275 | 276 | class Foo::Bar { Bar() { XXX(1) << "should work"; } }; 277 | -------------------------------------------------------------------------------- /test/foo.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "bar.h" 6 | #include "xss_module.h" 7 | 8 | 9 | typedef std::map Mapper; 10 | typedef struct { int zz; } AnonStruct; 11 | typedef union NamedUnion { bool xx; } *NamedPtr; 12 | 13 | class Baz; 14 | class Unused; 15 | class Local; 16 | class DataMember; 17 | class Ptr; 18 | 19 | class Data; 20 | extern Data* d; 21 | 22 | namespace somespace { 23 | class Jj; 24 | } 25 | 26 | namespace FooNS { 27 | 28 | class Foo : public Bar { 29 | public: 30 | explicit Foo(); 31 | Foo(register const unsigned long& a, Baz* output) {} 32 | virtual ~Foo() = 0; 33 | 34 | protected: 35 | virtual const somespace::Jj& Get() const; 36 | const somespace::Jj& Get2() const = 0; 37 | int Get3() const { return 0; } 38 | 39 | template 40 | T& foo(T* bar); 41 | 42 | private: 43 | void Method(const Mapper& mapper, somespace::Jj* j); 44 | Mapper& Blah(); 45 | Mapper data_; 46 | DataMember* data; 47 | auto_ptr ptr; 48 | }; 49 | 50 | void Function(const Foo& foo, Bar* bar); 51 | extern const ws::RMM *full; 52 | void Function2(const Local& local); 53 | 54 | } 55 | 56 | class FooModule : public XSS::Module { 57 | public: 58 | FooModule() : XSS::Module("mod") { } 59 | }; 60 | 61 | template class ProtoArray; 62 | 63 | class Experiment { 64 | explicit Experiment(); 65 | 66 | struct Foo { 67 | bool operator() (const Foo *a, const Foo *b) { 68 | return true; 69 | } 70 | }; 71 | 72 | private: 73 | }; 74 | 75 | 76 | namespace noname { 77 | enum nn { 78 | bar, 79 | }; 80 | } 81 | 82 | namespace { 83 | // Anon namespace 84 | } 85 | 86 | #include "bar.h" // foo 87 | #include // foo 88 | 89 | class Abc::Xyz {}; 90 | 91 | class Fool : public Bar, public Baz {}; 92 | 93 | 94 | namespace { 95 | // Anon namespace with something 96 | void lkjsdfkd(); 97 | } 98 | 99 | class TT : public TTbase {}; 100 | 101 | void (*ptof)(const TT& tt); 102 | 103 | typedef void(*Setter)(int*, int); 104 | 105 | // filling data. 106 | Fool data[] = { NULL, }; 107 | 108 | class def_var { 109 | static const int kConstant1 = 5; 110 | static const int kConstant = (kConstant2 - 1) / 4; 111 | }; 112 | 113 | #include "not-used.h" 114 | 115 | namespace { 116 | class LaunchNameEquals { 117 | public: 118 | bool operator()(void* launch) { 119 | return true; 120 | } 121 | }; 122 | } // namespace 123 | 124 | enum {boo}; 125 | 126 | class Blashdfdf { 127 | public: 128 | bool operator()(void* launch) const = 0; 129 | }; 130 | 131 | const char kBS[] = "~!@#$%^&*()-+{}|`[]\\:\";'<>?,./ =\t\r\n\v\f\177"; 132 | 133 | class FooOnYou { 134 | void (sq_type::*field_)(string&); 135 | }; 136 | 137 | namespace wtf = noname; 138 | 139 | void AnnotatedFunction() HOPEFULLY_THIS_IS_AN_ATTR; 140 | void RealAnnotatedFunction() __attribute__((weakref)); 141 | 142 | class TestAttrs { 143 | void AnnotatedMethod() HOPEFULLY_THIS_IS_AN_ATTR; 144 | void RealAnnotatedMethod() __attribute__((weakref)); 145 | }; 146 | 147 | typedef struct _IplImage IplImage; 148 | 149 | namespace { 150 | struct AR; 151 | class BS { }; 152 | } 153 | 154 | class OpCode { 155 | public: 156 | enum Operator { 157 | ID = 0, 158 | CASE = 1, 159 | DEL = 2, 160 | INS = 3, 161 | }; 162 | enum Operator op; 163 | }; 164 | 165 | class MemoryCache { 166 | mutable Mutex index_mutex_; 167 | struct CacheEntry **index_; 168 | }; 169 | 170 | struct lala : dense_hashmap {}; 171 | 172 | #if 0 173 | #if 1 174 | ignore more stuff a second apostrophe here does not work 175 | #else 176 | something else 177 | #endif 178 | #endif 179 | 180 | void Atmak() throw(std::bull); 181 | 182 | enum Status; 183 | enum RPC::Status2; 184 | 185 | typedef class ABC XYZ123; 186 | typedef class CB LP; 187 | typedef class OhMy {} JeeziePeezy; 188 | 189 | template 190 | struct HashIndexSpec { 191 | HashIndexSpec() {} 192 | private: 193 | }; 194 | 195 | template 196 | class ThreadShared::Accessor { 197 | public: 198 | }; 199 | 200 | class FooStr { 201 | char mystr[sizeof(mystr)]; 202 | }; 203 | 204 | class FooStruct { 205 | struct in_addr addr_; 206 | }; 207 | 208 | enum {UNKNOWN = 0, GARBAGE_COLLECTING, NOT_GARBAGE_COLLECTING} garbage_state; 209 | 210 | class InlineClass { 211 | class File* file_; 212 | class File* Method1(); 213 | File* Method2(); 214 | }; 215 | 216 | typedef enum A::D FT; 217 | 218 | #if 0 219 | @ 220 | #endif 221 | #include "dir//bar.h" 222 | 223 | const string single_backslash("\\"); 224 | 225 | struct BitFields { 226 | int done; 227 | } flags_; 228 | 229 | class MyF { 230 | template 231 | friend class shared_ptr; 232 | }; 233 | 234 | class Colon { 235 | virtual ::Foo GetFoo(); 236 | ::Foo GetFoo2(); 237 | friend ::Foo& fn(); 238 | }; 239 | 240 | string foo = "\nX=\"/*\""; 241 | 242 | struct streamer::Key GlobalKey; 243 | typedef time_t (* TimeFunc)(time_t*); 244 | typedef char dummy_des_key_schedule[128]; 245 | 246 | class Copy { 247 | public: 248 | Copy(); // normal ctor 249 | Copy& operator=(const Copy& other); // copy ctor 250 | }; 251 | 252 | Copy& Copy::operator=(const Copy& rhs) 253 | { 254 | return *this; 255 | } 256 | 257 | struct Flag; 258 | class StructMethod { 259 | struct Flag *flag(); 260 | }; 261 | 262 | template class TemplateCtorDtor { 263 | public: 264 | TemplateCtorDtor(T *ptr); 265 | ~TemplateCtorDtor(); 266 | }; 267 | 268 | template 269 | Type* Singleton::instance_ = NULL; 270 | 271 | int CreateFakeResults(bool add, float score = 0.5); 272 | 273 | map ReturnFlags(); 274 | 275 | vector string_list; 276 | 277 | class Foo::Bar { Bar() { XXX(1) << "should work"; } }; 278 | 279 | class Optional { 280 | void getValue() & { } 281 | void getValue() && { } 282 | }; 283 | 284 | class TypeBuilder : public Base*, false> {}; 285 | 286 | int* Foo::Get(){} 287 | int** Foo::Get(){} 288 | int*** Foo::Get(){} 289 | 290 | typedef boost::function&, uint32_t, bool)> ReadFinishedFunc; 291 | struct is_hashable_data : integral_constant<((42))> {}; 292 | 293 | extern "C" {} 294 | 295 | class Resource 296 | { 297 | OGRE_AUTO_MUTEX 298 | class Listener 299 | { 300 | }; 301 | }; 302 | 303 | static_assert(21 < 42, ""); 304 | 305 | class Impl; 306 | void fn(Impl& impl); 307 | #include "foo.hxx" 308 | -------------------------------------------------------------------------------- /cppclean: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2007 Neal Norwitz 4 | # Portions Copyright 2007 Google Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """Find warnings for C++ code.""" 19 | 20 | from __future__ import absolute_import 21 | from __future__ import print_function 22 | from __future__ import unicode_literals 23 | 24 | import argparse 25 | import fnmatch 26 | import os 27 | import sys 28 | 29 | from cpp import __version__ 30 | from cpp import ast 31 | from cpp import find_warnings 32 | from cpp import nonvirtual_dtors 33 | from cpp import static_data 34 | from cpp import tokenize 35 | from cpp import utils 36 | 37 | 38 | def match_file(filename, exclude_patterns): 39 | """Return True if file is a C++ file or a directory.""" 40 | base_name = os.path.basename(filename) 41 | 42 | if base_name.startswith('.'): 43 | return False 44 | 45 | for pattern in exclude_patterns: 46 | if fnmatch.fnmatch(base_name, pattern): 47 | return False 48 | 49 | if find_warnings.is_header_file(filename): 50 | return True 51 | 52 | if find_warnings.is_cpp_file(filename): 53 | return True 54 | 55 | if os.path.isdir(filename): 56 | return True 57 | 58 | return False 59 | 60 | 61 | def find_files(filenames, exclude_patterns): 62 | """Yield filenames.""" 63 | while filenames: 64 | name = filenames.pop(0) 65 | if os.path.isdir(name): 66 | for root, directories, children in os.walk(name): 67 | filenames += [os.path.join(root, f) for f in sorted(children) 68 | if match_file(os.path.join(root, f), 69 | exclude_patterns)] 70 | directories[:] = [d for d in directories 71 | if match_file(os.path.join(root, d), 72 | exclude_patterns)] 73 | else: 74 | yield name 75 | 76 | 77 | def main(): 78 | parser = argparse.ArgumentParser() 79 | parser.add_argument('files', nargs='+') 80 | parser.add_argument('--exclude', action='append', 81 | dest='exclude_patterns', default=[], metavar='pattern', 82 | help='exclude files matching this pattern; ' 83 | 'specify this multiple times for multiple ' 84 | 'patterns') 85 | parser.add_argument('--include-path', '-i', '-I', action='append', 86 | dest='include_paths', default=[], 87 | metavar='path', 88 | help='add a header include path; ' 89 | 'specify this multiple times for multiple ' 90 | 'include paths') 91 | parser.add_argument('--include-path-system', '-s', action='append', 92 | dest='include_system_paths', default=[], 93 | metavar='sys_path', 94 | help='same as --include-path but explicitly ' 95 | 'designates all header files found in these ' 96 | 'directories as "system" includes') 97 | parser.add_argument('--include-path-non-system', '-n', 98 | action='append', dest='include_nonsystem_paths', 99 | metavar='nonsys_path', 100 | default=[], 101 | help='same as --include-path but explicitly ' 102 | 'designates all header files found in these ' 103 | 'directories as "non-system" includes') 104 | parser.add_argument('--verbose', action='store_true', 105 | help='print verbose messages') 106 | parser.add_argument('--version', action='version', 107 | version='%(prog)s ' + __version__) 108 | parser.add_argument('--quiet', '-q', action='store_true', 109 | help='ignore parse errors') 110 | args = parser.parse_args() 111 | 112 | # For Python 2 where argparse does not return Unicode. 113 | args.files = [filename.decode(sys.getfilesystemencoding()) 114 | if hasattr(filename, 'decode') else filename 115 | for filename in args.files] 116 | 117 | all_includes = list(set( 118 | args.include_paths + args.include_system_paths + 119 | args.include_nonsystem_paths)) 120 | 121 | status = 0 122 | for filename in ( 123 | sorted(find_files(args.files, 124 | exclude_patterns=args.exclude_patterns)) 125 | ): 126 | if args.verbose: 127 | print('Processing', filename, file=sys.stderr) 128 | 129 | try: 130 | source = utils.read_file(filename) 131 | if source is None: 132 | continue 133 | 134 | builder = ast.builder_from_source(source, 135 | filename, 136 | args.include_system_paths, 137 | args.include_nonsystem_paths, 138 | quiet=args.quiet) 139 | entire_ast = list([_f for _f in builder.generate() if _f]) 140 | except tokenize.TokenError as exception: 141 | if args.verbose: 142 | print('{}: token error: {}'.format(filename, exception), 143 | file=sys.stderr) 144 | continue 145 | except (ast.ParseError, 146 | UnicodeDecodeError) as exception: 147 | if not args.quiet: 148 | print('{}: parsing error: {}'.format(filename, exception), 149 | file=sys.stderr) 150 | continue 151 | 152 | for module in [find_warnings, 153 | nonvirtual_dtors, 154 | static_data]: 155 | if module.run(filename, source, entire_ast, 156 | include_paths=all_includes, 157 | system_include_paths=args.include_system_paths, 158 | nonsystem_include_paths=args.include_nonsystem_paths, 159 | quiet=args.quiet): 160 | status = 1 161 | 162 | return status 163 | 164 | 165 | try: 166 | sys.exit(main()) 167 | except KeyboardInterrupt: 168 | sys.exit(1) 169 | -------------------------------------------------------------------------------- /cpp/symbols.py: -------------------------------------------------------------------------------- 1 | # Copyright 2007 Neal Norwitz 2 | # Portions Copyright 2007 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Symbol Table utility code.""" 17 | 18 | from __future__ import absolute_import 19 | from __future__ import unicode_literals 20 | 21 | 22 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 23 | 24 | 25 | class Error(BaseException): 26 | 27 | """Exception raised when lookup fails.""" 28 | 29 | 30 | class Symbol(object): 31 | 32 | """Data container used internally.""" 33 | 34 | def __init__(self, name, parts, namespace_stack): 35 | self.name = name 36 | self.parts = parts 37 | self.namespace_stack = namespace_stack 38 | 39 | 40 | class SymbolTable(object): 41 | 42 | """Symbol table that can perform namespace operations.""" 43 | 44 | def __init__(self): 45 | # None is the global namespace. 46 | self.namespaces = {None: {}} 47 | 48 | def _lookup_namespace(self, symbol, namespace): 49 | """Helper for lookup_symbol that only looks up variables in a 50 | namespace. 51 | 52 | Args: 53 | symbol: Symbol 54 | namespace: pointer into self.namespaces 55 | """ 56 | for namespace_part in symbol.parts: 57 | namespace = namespace.get(namespace_part) 58 | if namespace is None: 59 | break 60 | if not isinstance(namespace, dict): 61 | return namespace 62 | raise Error('%s not found' % symbol.name) 63 | 64 | def _lookup_global(self, symbol): 65 | """Helper for lookup_symbol that only looks up global variables. 66 | 67 | Args: 68 | symbol: Symbol 69 | """ 70 | assert symbol.parts 71 | namespace = self.namespaces 72 | if len(symbol.parts) == 1: 73 | # If there is only one part, look in globals. 74 | namespace = self.namespaces[None] 75 | try: 76 | # Try to do a normal, global namespace lookup. 77 | return self._lookup_namespace(symbol, namespace) 78 | except Error as orig_exc: 79 | try: 80 | # The normal lookup can fail if all of the parts aren't 81 | # namespaces. This happens with OuterClass::Inner. 82 | namespace = self.namespaces[None] 83 | return self._lookup_namespace(symbol, namespace) 84 | except Error: 85 | raise orig_exc 86 | 87 | def _lookup_in_all_namespaces(self, symbol): 88 | """Helper for lookup_symbol that looks for symbols in all namespaces. 89 | 90 | Args: 91 | symbol: Symbol 92 | """ 93 | namespace = self.namespaces 94 | # Create a stack of namespaces. 95 | namespace_stack = [] 96 | for current in symbol.namespace_stack: 97 | namespace = namespace.get(current) 98 | if namespace is None or not isinstance(namespace, dict): 99 | break 100 | namespace_stack.append(namespace) 101 | 102 | # Iterate through the stack in reverse order. Need to go from 103 | # innermost namespace to outermost. 104 | for namespace in reversed(namespace_stack): 105 | try: 106 | return self._lookup_namespace(symbol, namespace) 107 | except Error: 108 | pass 109 | return None 110 | 111 | def lookup_symbol(self, name, namespace_stack): 112 | """Returns AST node and module for symbol if found. 113 | 114 | Args: 115 | name: 'name of the symbol to lookup' 116 | namespace_stack: None or ['namespaces', 'in', 'current', 'scope'] 117 | 118 | Returns: 119 | (ast.Node, module (ie, any object stored with symbol)) if found 120 | 121 | Raises: 122 | Error if the symbol cannot be found. 123 | """ 124 | # TODO(nnorwitz): a convenient API for this depends on the 125 | # representation of the name. e.g., does symbol_name contain 126 | # ::, is symbol_name a list of colon separated names, how are 127 | # names prefixed with :: handled. These have different lookup 128 | # semantics (if leading ::) or change the desirable API. 129 | 130 | # For now assume that the symbol_name contains :: and parse it. 131 | symbol = Symbol(name, name.split('::'), namespace_stack) 132 | assert symbol.parts 133 | if symbol.parts[0] == '': 134 | # Handle absolute (global) ::symbol_names. 135 | symbol.parts = symbol.parts[1:] 136 | elif namespace_stack is not None: 137 | result = self._lookup_in_all_namespaces(symbol) 138 | if result: 139 | return result 140 | 141 | return self._lookup_global(symbol) 142 | 143 | def _add(self, symbol_name, namespace, node, module): 144 | """Helper function for adding symbols. 145 | 146 | See add_symbol(). 147 | """ 148 | result = symbol_name in namespace 149 | namespace[symbol_name] = node, module 150 | return not result 151 | 152 | def add_symbol(self, symbol_name, namespace_stack, node, module): 153 | """Adds symbol_name defined in namespace_stack to the symbol table. 154 | 155 | Args: 156 | symbol_name: 'name of the symbol to lookup' 157 | namespace_stack: None or ['namespaces', 'symbol', 'defined', 'in'] 158 | node: ast.Node that defines this symbol 159 | module: module (any object) this symbol is defined in 160 | 161 | Returns: 162 | bool(if symbol was *not* already present) 163 | """ 164 | # TODO(nnorwitz): verify symbol_name doesn't contain :: ? 165 | if namespace_stack: 166 | # Handle non-global symbols (ie, in some namespace). 167 | last_namespace = self.namespaces 168 | for namespace in namespace_stack: 169 | last_namespace = last_namespace.setdefault(namespace, {}) 170 | else: 171 | last_namespace = self.namespaces[None] 172 | return self._add(symbol_name, last_namespace, node, module) 173 | 174 | def get_namespace(self, name_seq): 175 | """Returns the prefix of names from name_seq that are known namespaces. 176 | 177 | Args: 178 | name_seq: ['names', 'of', 'possible', 'namespace', 'to', 'find'] 179 | 180 | Returns: 181 | ['names', 'that', 'are', 'namespaces', 'possibly', 'empty', 'list'] 182 | """ 183 | namespaces = self.namespaces 184 | result = [] 185 | for name in name_seq: 186 | namespaces = namespaces.get(name) 187 | if not namespaces: 188 | break 189 | result.append(name) 190 | return result 191 | -------------------------------------------------------------------------------- /test_symbols.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2007 Neal Norwitz 4 | # Portions Copyright 2007 Google Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """Symbol Table test.""" 19 | 20 | from __future__ import absolute_import 21 | 22 | import unittest 23 | 24 | from cpp import symbols 25 | 26 | 27 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 28 | 29 | 30 | class SymbolTableTest(unittest.TestCase): 31 | 32 | def _add_symbol(self, st, name, ns_stack): 33 | """Helper for the lookup_symbol test methods.""" 34 | node = object() 35 | module = object() 36 | st.add_symbol(name, ns_stack, node, module) 37 | return node, module 38 | 39 | def test_lookup_symbol_with_global_that_does_not_exist(self): 40 | st = symbols.SymbolTable() 41 | self.assertRaises(symbols.Error, st.lookup_symbol, 'foo', None) 42 | 43 | def test_lookup_symbol_with_namespace_that_does_not_exist(self): 44 | st = symbols.SymbolTable() 45 | self.assertRaises(symbols.Error, st.lookup_symbol, 'foo', ['n']) 46 | 47 | def test_lookup_symbol_with_global_that_exists(self): 48 | st = symbols.SymbolTable() 49 | node, module = self._add_symbol(st, 'foo', None) 50 | self.assertEqual((node, module), st.lookup_symbol('foo', None)) 51 | 52 | def test_lookup_symbol_with_complex_global_that_exists(self): 53 | st = symbols.SymbolTable() 54 | node, module = self._add_symbol(st, 'foo', ['ns1', 'ns2']) 55 | self.assertEqual((node, module), 56 | st.lookup_symbol('::ns1::ns2::foo', None)) 57 | self.assertEqual((node, module), 58 | st.lookup_symbol('ns1::ns2::foo', None)) 59 | 60 | def test_lookup_symbol_in_namespaces(self): 61 | st = symbols.SymbolTable() 62 | 63 | # 3 nested namespaces, all contain the same symbol (foo). 64 | ns = ['ns1', 'ns2', 'ns3'] 65 | add_symbol = self._add_symbol 66 | # Also add foo to the global namespace. 67 | ns_symbols = [add_symbol(st, 'foo', None)] + \ 68 | [add_symbol(st, 'foo', ns[:i + 1]) 69 | for i in range(len(ns))] 70 | 71 | # Verify global lookup works. 72 | self.assertEqual(ns_symbols[0], st.lookup_symbol('::foo', ns)) 73 | 74 | # Verify looking up relative symbols work. 75 | self.assertEqual(ns_symbols[1], st.lookup_symbol('foo', ns[:1])) 76 | self.assertEqual(ns_symbols[2], st.lookup_symbol('foo', ns[:2])) 77 | self.assertEqual(ns_symbols[3], st.lookup_symbol('foo', ns[:3])) 78 | bigger = ns + ['ns4', 'ns5'] 79 | self.assertEqual(ns_symbols[3], st.lookup_symbol('foo', bigger)) 80 | 81 | # Remove ns2 and verify that when looking for foo in ns2 it finds ns1. 82 | ns1 = st.namespaces['ns1'] 83 | del ns1['ns2'] 84 | self.assertEqual(ns_symbols[1], st.lookup_symbol('foo', ns[:2])) 85 | 86 | def test_add(self): 87 | st = symbols.SymbolTable() 88 | node = object() 89 | module = object() 90 | namespace = {} 91 | symbol_name = 'foo' 92 | 93 | self.assertEqual(True, st._add(symbol_name, namespace, node, module)) 94 | self.assertEqual(1, len(namespace)) 95 | self.assertEqual(['foo'], list(namespace)) 96 | 97 | # Adding again should return False. 98 | self.assertEqual(False, st._add(symbol_name, namespace, node, module)) 99 | 100 | def test_add_symbol_in_global_namespace(self): 101 | st = symbols.SymbolTable() 102 | node = object() 103 | module = object() 104 | ns_stack = None 105 | name = 'foo' 106 | 107 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 108 | # Verify the symbol was added properly to the symbol table namespaces. 109 | self.assertTrue('foo' in st.namespaces[None]) 110 | self.assertEqual((node, module), st.namespaces[None]['foo']) 111 | 112 | # Already added, verify we get false. 113 | self.assertEqual(False, st.add_symbol(name, ns_stack, node, module)) 114 | 115 | def test_add_symbol_in_namespace_with_one_level(self): 116 | st = symbols.SymbolTable() 117 | node = object() 118 | module = object() 119 | ns_stack = ['ns-foo'] 120 | name = 'foo' 121 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 122 | # Verify the symbol was added properly to the symbol table namespaces. 123 | self.assertTrue('ns-foo' in st.namespaces) 124 | self.assertTrue('foo' in st.namespaces['ns-foo']) 125 | self.assertEqual((node, module), st.namespaces['ns-foo']['foo']) 126 | 127 | # Already added, verify we get false. 128 | self.assertEqual(False, st.add_symbol(name, ns_stack, node, module)) 129 | 130 | def test_add_symbol_in_namespace_with_three_levels(self): 131 | st = symbols.SymbolTable() 132 | node = object() 133 | module = object() 134 | ns_stack = ['ns1', 'ns2', 'ns3'] 135 | name = 'foo' 136 | 137 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 138 | # Verify the symbol was added properly to the symbol table namespaces. 139 | self.assertTrue('ns1' in st.namespaces) 140 | self.assertTrue('ns2' in st.namespaces['ns1']) 141 | self.assertTrue('ns3' in st.namespaces['ns1']['ns2']) 142 | self.assertTrue('foo' in st.namespaces['ns1']['ns2']['ns3']) 143 | self.assertEqual((node, module), 144 | st.namespaces['ns1']['ns2']['ns3']['foo']) 145 | 146 | # Now add something to ns1 and verify. 147 | ns_stack = ['ns1'] 148 | name = 'something' 149 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 150 | self.assertTrue('something' in st.namespaces['ns1']) 151 | self.assertEqual((node, module), st.namespaces['ns1']['something']) 152 | 153 | # Now add something to ns1::ns2 and verify. 154 | ns_stack = ['ns1', 'ns2'] 155 | name = 'else' 156 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 157 | self.assertTrue('else' in st.namespaces['ns1']['ns2']) 158 | self.assertEqual((node, module), st.namespaces['ns1']['ns2']['else']) 159 | 160 | # Now add something to the global namespace and verify. 161 | ns_stack = None 162 | name = 'global' 163 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 164 | self.assertTrue('global' in st.namespaces[None]) 165 | self.assertEqual((node, module), st.namespaces[None]['global']) 166 | 167 | # Verify table still has 2 elements (global namespace and ::ns1). 168 | self.assertEqual(2, len(st.namespaces)) 169 | # Verify ns1 still has 2 elements (ns2 and 'something'). 170 | self.assertEqual(2, len(st.namespaces['ns1'])) 171 | # Verify ns2 still has 2 elements (ns3 and 'else'). 172 | self.assertEqual(2, len(st.namespaces['ns1']['ns2'])) 173 | # Verify ns3 still has 1 element ('foo'). 174 | self.assertEqual(1, len(st.namespaces['ns1']['ns2']['ns3'])) 175 | 176 | def test_get_namespace(self): 177 | # Setup. 178 | st = symbols.SymbolTable() 179 | node = object() 180 | module = object() 181 | ns_stack = ['ns1', 'ns2', 'ns3'] 182 | name = 'foo' 183 | self.assertEqual(True, st.add_symbol(name, ns_stack, node, module)) 184 | 185 | # Verify. 186 | self.assertEqual([], st.get_namespace([])) 187 | self.assertEqual(['ns1'], st.get_namespace(['ns1'])) 188 | self.assertEqual(['ns1'], st.get_namespace(['ns1', 'foo'])) 189 | self.assertEqual(['ns1'], st.get_namespace(['ns1', 'foo'])) 190 | self.assertEqual(['ns1'], st.get_namespace(['ns1', 'foo', 'ns2'])) 191 | self.assertEqual(['ns1', 'ns2'], st.get_namespace(['ns1', 'ns2'])) 192 | self.assertEqual(['ns1', 'ns2'], st.get_namespace(['ns1', 'ns2', 'f'])) 193 | self.assertEqual(['ns1', 'ns2'], st.get_namespace(['ns1', 'ns2', 'f'])) 194 | self.assertEqual(['ns1', 'ns2', 'ns3'], 195 | st.get_namespace(['ns1', 'ns2', 'ns3', 'f'])) 196 | 197 | 198 | if __name__ == '__main__': 199 | unittest.main() 200 | -------------------------------------------------------------------------------- /cpp/tokenize.py: -------------------------------------------------------------------------------- 1 | # Copyright 2007 Neal Norwitz 2 | # Portions Copyright 2007 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Tokenize C++ source code.""" 17 | 18 | from __future__ import absolute_import 19 | from __future__ import print_function 20 | from __future__ import unicode_literals 21 | 22 | 23 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 24 | 25 | 26 | # Add $ as a valid identifier char since so much code uses it. 27 | _letters = 'abcdefghijklmnopqrstuvwxyz' 28 | _valid_identifier_first_char = _letters + _letters.upper() + '_$' 29 | _valid_identifier_char = _valid_identifier_first_char + '0123456789' 30 | VALID_IDENTIFIER_FIRST_CHARS = frozenset(_valid_identifier_first_char) 31 | VALID_IDENTIFIER_CHARS = frozenset(_valid_identifier_char) 32 | HEX_DIGITS = frozenset('0123456789abcdefABCDEF') 33 | INT_OR_FLOAT_DIGITS = frozenset('01234567890eE-+') 34 | 35 | 36 | # C++0x string prefixes. 37 | _STR_PREFIXES = frozenset(('R', 'u8', 'u8R', 'u', 'uR', 'U', 'UR', 'L', 'LR')) 38 | 39 | 40 | # Token types. 41 | UNKNOWN = 'UNKNOWN' 42 | SYNTAX = 'SYNTAX' 43 | CONSTANT = 'CONSTANT' 44 | NAME = 'NAME' 45 | PREPROCESSOR = 'PREPROCESSOR' 46 | 47 | 48 | class TokenError(Exception): 49 | 50 | """Raised when tokenization fails.""" 51 | 52 | 53 | class Token(object): 54 | 55 | """Data container to represent a C++ token. 56 | 57 | Tokens can be identifiers, syntax char(s), constants, or 58 | pre-processor directives. 59 | 60 | start contains the index of the first char of the token in the source 61 | end contains the index of the last char of the token in the source 62 | """ 63 | 64 | def __init__(self, token_type, name, start, end): 65 | self.token_type = token_type 66 | self.name = name 67 | self.start = start 68 | self.end = end 69 | 70 | def __str__(self): 71 | return 'Token(%r, %s, %s)' % (self.name, self.start, self.end) 72 | 73 | __repr__ = __str__ 74 | 75 | 76 | def _get_string(source, i): 77 | i = source.find('"', i + 1) 78 | while source[i - 1] == '\\': 79 | # Count the trailing backslashes. 80 | backslash_count = 1 81 | j = i - 2 82 | while source[j] == '\\': 83 | backslash_count += 1 84 | j -= 1 85 | # When trailing backslashes are even, they escape each other. 86 | if (backslash_count % 2) == 0: 87 | break 88 | i = source.find('"', i + 1) 89 | return i + 1 90 | 91 | 92 | def _get_char(source, start, i): 93 | # NOTE(nnorwitz): may not be quite correct, should be good enough. 94 | i = source.find("'", i + 1) 95 | while i != -1 and source[i - 1] == '\\': 96 | # Need to special case '\\'. 97 | if source[i - 2] == '\\': 98 | break 99 | i = source.find("'", i + 1) 100 | # Try to handle unterminated single quotes. 101 | return i + 1 if i != -1 else start + 1 102 | 103 | 104 | def get_tokens(source): 105 | """Returns a sequence of Tokens. 106 | 107 | Args: 108 | source: string of C++ source code. 109 | 110 | Yields: 111 | Token that represents the next token in the source. 112 | """ 113 | if not source.endswith('\n'): 114 | source += '\n' 115 | 116 | # Cache various valid character sets for speed. 117 | valid_identifier_first_chars = VALID_IDENTIFIER_FIRST_CHARS 118 | valid_identifier_chars = VALID_IDENTIFIER_CHARS 119 | hex_digits = HEX_DIGITS 120 | int_or_float_digits = INT_OR_FLOAT_DIGITS 121 | int_or_float_digits2 = int_or_float_digits | set('.') 122 | 123 | # Ignore tokens while in a #if 0 block. 124 | count_ifs = 0 125 | 126 | i = 0 127 | end = len(source) 128 | while i < end: 129 | # Skip whitespace. 130 | while i < end and source[i].isspace(): 131 | i += 1 132 | if i >= end: 133 | return 134 | 135 | token_type = UNKNOWN 136 | start = i 137 | c = source[i] 138 | # Find a string token. 139 | if c in valid_identifier_first_chars or c == '_': 140 | token_type = NAME 141 | while source[i] in valid_identifier_chars: 142 | i += 1 143 | # String and character constants can look like a name if 144 | # they are something like L"". 145 | if source[i] == "'" and source[start:i] in _STR_PREFIXES: 146 | token_type = CONSTANT 147 | i = _get_char(source, start, i) 148 | elif source[i] == '"' and source[start:i] in _STR_PREFIXES: 149 | token_type = CONSTANT 150 | i = _get_string(source, i) 151 | elif c == '/' and source[i + 1] == '/': # Find // comments. 152 | i = _find(source, '\n', i) 153 | continue 154 | elif c == '/' and source[i + 1] == '*': # Find /* comments. */ 155 | i = _find(source, '*/', i) + 2 156 | continue 157 | elif c in '<>': # Handle '<' and '>' tokens. 158 | token_type = SYNTAX 159 | i += 1 160 | new_ch = source[i] 161 | # Do not merge '>>' or '>>=' into a single token 162 | if new_ch == c and c != '>': 163 | i += 1 164 | new_ch = source[i] 165 | if new_ch == '=': 166 | i += 1 167 | elif c in ':+-&|=': # Handle 'XX' and 'X=' tokens. 168 | token_type = SYNTAX 169 | i += 1 170 | new_ch = source[i] 171 | if new_ch == c: 172 | i += 1 173 | elif c == '-' and new_ch == '>': 174 | i += 1 175 | elif new_ch == '=': 176 | i += 1 177 | elif c in '!*^%/': # Handle 'X=' tokens. 178 | token_type = SYNTAX 179 | i += 1 180 | new_ch = source[i] 181 | if new_ch == '=': 182 | i += 1 183 | elif c in '()[]{}~?;.,': # Handle single char tokens. 184 | token_type = SYNTAX 185 | i += 1 186 | if c == '.' and source[i].isdigit(): 187 | token_type = CONSTANT 188 | i += 1 189 | while source[i] in int_or_float_digits: 190 | i += 1 191 | # Handle float suffixes. 192 | for suffix in ('l', 'f'): 193 | if suffix == source[i:i + 1].lower(): 194 | i += 1 195 | break 196 | elif c.isdigit(): # Find integer. 197 | token_type = CONSTANT 198 | if c == '0' and source[i + 1] in 'xX': 199 | # Handle hex digits. 200 | i += 2 201 | while source[i] in hex_digits: 202 | i += 1 203 | else: 204 | while source[i] in int_or_float_digits2: 205 | i += 1 206 | # Handle integer (and float) suffixes. 207 | if source[i].isalpha(): 208 | for suffix in ('ull', 'll', 'ul', 'l', 'f', 'u'): 209 | size = len(suffix) 210 | if suffix == source[i:i + size].lower(): 211 | i += size 212 | break 213 | elif c == '"': # Find string. 214 | token_type = CONSTANT 215 | i = _get_string(source, i) 216 | elif c == "'": # Find char. 217 | token_type = CONSTANT 218 | i = _get_char(source, start, i) 219 | elif c == '#': # Find pre-processor command. 220 | token_type = PREPROCESSOR 221 | got_if = source[i:i + 3] == '#if' 222 | if count_ifs and source[i:i + 6] == '#endif': 223 | count_ifs -= 1 224 | if count_ifs == 0: 225 | source = source[:i].ljust(i + 6) + source[i + 6:] 226 | continue 227 | 228 | # Handle preprocessor statements (\ continuations). 229 | while True: 230 | i1 = source.find('\n', i) 231 | i2 = source.find('//', i) 232 | i3 = source.find('/*', i) 233 | i4 = source.find('"', i) 234 | # Get the first important symbol (newline, comment, EOF/end). 235 | i = min([x for x in (i1, i2, i3, i4, end) if x != -1]) 236 | 237 | # Handle comments in #define macros. 238 | if i == i3: 239 | i = _find(source, '*/', i) + 2 240 | source = source[:i3].ljust(i) + source[i:] 241 | continue 242 | 243 | # Handle #include "dir//foo.h" properly. 244 | if source[i] == '"': 245 | i = _find(source, '"', i + 1) + 1 246 | continue 247 | 248 | # Keep going if end of the line and the line ends with \. 249 | if i == i1 and source[i - 1] == '\\': 250 | i += 1 251 | continue 252 | 253 | if got_if: 254 | begin = source.find('(', start, i) 255 | if begin == -1: 256 | begin = source.find(' ', start) 257 | begin = begin + 1 258 | s1 = source.find(' ', begin) 259 | s2 = source.find(')', begin) 260 | s3 = source.find('\n', begin) 261 | s = min([x for x in (s1, s2, s3, end) if x != -1]) 262 | 263 | condition = source[begin:s] 264 | if ( 265 | count_ifs or 266 | condition == '0' or 267 | condition == '__OBJC__' 268 | ): 269 | count_ifs += 1 270 | break 271 | elif c == '\\': # Handle \ in code. 272 | # This is different from the pre-processor \ handling. 273 | i += 1 274 | continue 275 | elif count_ifs: 276 | # Ignore bogus code when we are inside an #if block. 277 | i += 1 278 | continue 279 | else: 280 | raise TokenError("unexpected token '{0}'".format(c)) 281 | 282 | if count_ifs: 283 | continue 284 | 285 | assert i > 0 286 | yield Token(token_type, source[start:i], start, i) 287 | 288 | 289 | def _find(string, sub_string, start_index): 290 | """Return index of sub_string in string. 291 | 292 | Raise TokenError if sub_string is not found. 293 | """ 294 | result = string.find(sub_string, start_index) 295 | if result == -1: 296 | raise TokenError("expected '{0}'".format(sub_string)) 297 | return result 298 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [2007] Neal Norwitz 191 | Portions Copyright [2007] Google Inc. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /test_tokenize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2007 Neal Norwitz 4 | # Portions Copyright 2007 Google Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """Tokenize test.""" 19 | 20 | from __future__ import absolute_import 21 | 22 | import unittest 23 | 24 | from cpp import tokenize 25 | 26 | 27 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 28 | 29 | 30 | # For convenience, add factories and __eq__ to test the module. 31 | 32 | 33 | def Syntax(name, start, end): 34 | return tokenize.Token(tokenize.SYNTAX, name, start, end) 35 | 36 | 37 | def Constant(name, start, end): 38 | return tokenize.Token(tokenize.CONSTANT, name, start, end) 39 | 40 | 41 | def Name(name, start, end): 42 | return tokenize.Token(tokenize.NAME, name, start, end) 43 | 44 | 45 | def Preprocessor(name, start, end): 46 | return tokenize.Token(tokenize.PREPROCESSOR, name, start, end) 47 | 48 | 49 | def __eq__(self, other): 50 | assert isinstance(other, self.__class__) 51 | return (self.token_type == other.token_type and 52 | self.name == other.name and 53 | self.start == other.start and 54 | self.end == other.end) 55 | 56 | 57 | tokenize.Token.__eq__ = __eq__ 58 | 59 | 60 | class TokenizeTest(unittest.TestCase): 61 | 62 | def get_tokens(self, string): 63 | return list(tokenize.get_tokens(string)) 64 | 65 | def test_get_tokens_empty_string(self): 66 | self.assertEqual([], self.get_tokens('')) 67 | 68 | def test_get_tokens_whitespace(self): 69 | self.assertEqual([], self.get_tokens(' ')) 70 | self.assertEqual([], self.get_tokens(' \n\n\n')) 71 | 72 | def test_get_tokens_cpp_comment(self): 73 | self.assertEqual([], self.get_tokens('// comment')) 74 | 75 | def test_get_tokens_multiline_comment(self): 76 | self.assertEqual([], self.get_tokens('/* comment\n\n\nfoo */')) 77 | 78 | def test_get_tokens_if0(self): 79 | tokens = self.get_tokens('#if 0\n@\n#endif') 80 | self.assertEqual(0, len(tokens), tokens) 81 | 82 | def test_get_tokens_define(self): 83 | tokens = self.get_tokens('#define PI 3.14') 84 | self.assertEqual(1, len(tokens), tokens) 85 | self.assertEqual(Preprocessor('#define PI 3.14', 0, 15), tokens[0]) 86 | 87 | def test_get_tokens_binary_operators(self): 88 | for operator in '+-*/%&|^<>': 89 | # 012 345 90 | tokens = self.get_tokens('5 %s 3' % operator) 91 | self.assertEqual(3, len(tokens), tokens) 92 | self.assertEqual(Constant('5', 0, 1), tokens[0]) 93 | self.assertEqual(Syntax(operator, 2, 3), tokens[1]) 94 | self.assertEqual(Constant('3', 4, 5), tokens[2]) 95 | 96 | def test_get_tokens_multi_char_binary_operators(self): 97 | # 0123456 98 | tokens = self.get_tokens('5 << 3') 99 | self.assertEqual(3, len(tokens), tokens) 100 | self.assertEqual(Constant('5', 0, 1), tokens[0]) 101 | self.assertEqual(Syntax('<<', 2, 4), tokens[1]) 102 | self.assertEqual(Constant('3', 5, 6), tokens[2]) 103 | 104 | def test_get_tokens_addition_with_comment(self): 105 | # 0123456789012 3 4 56789012345 106 | tokens = self.get_tokens('5 /* comment\n\n\nfoo */ + 3') 107 | self.assertEqual(3, len(tokens), tokens) 108 | self.assertEqual(Constant('5', 0, 1), tokens[0]) 109 | self.assertEqual(Syntax('+', 22, 23), tokens[1]) 110 | self.assertEqual(Constant('3', 24, 25), tokens[2]) 111 | 112 | def test_get_tokens_logical_operators(self): 113 | for operator in ('&&', '||'): 114 | # 0123456 115 | tokens = self.get_tokens('a %s b' % operator) 116 | self.assertEqual(3, len(tokens), tokens) 117 | self.assertEqual(Name('a', 0, 1), tokens[0]) 118 | self.assertEqual(Syntax(operator, 2, 4), tokens[1]) 119 | self.assertEqual(Name('b', 5, 6), tokens[2]) 120 | 121 | # 01234 122 | tokens = self.get_tokens('!not') 123 | self.assertEqual(2, len(tokens), tokens) 124 | self.assertEqual(Syntax('!', 0, 1), tokens[0]) 125 | self.assertEqual(Name('not', 1, 4), tokens[1]) 126 | 127 | def test_get_tokens_operators(self): 128 | for operator in ('+=', '-=', '*=', '==', '!=', '/=', '%=', '^=', '|=', 129 | '<<', '<=', '>='): 130 | # 0123456 131 | tokens = self.get_tokens('a %s b' % operator) 132 | self.assertEqual(3, len(tokens), tokens) 133 | self.assertEqual(Name('a', 0, 1), tokens[0]) 134 | self.assertEqual(Syntax(operator, 2, 4), tokens[1]) 135 | self.assertEqual(Name('b', 5, 6), tokens[2]) 136 | 137 | def test_get_tokens_ones_complement(self): 138 | # 01234 139 | tokens = self.get_tokens('~not') 140 | self.assertEqual(2, len(tokens), tokens) 141 | self.assertEqual(Syntax('~', 0, 1), tokens[0]) 142 | self.assertEqual(Name('not', 1, 4), tokens[1]) 143 | 144 | def test_get_tokens_pre_increment_operators(self): 145 | for operator in ('++', '--'): 146 | # 012345 147 | tokens = self.get_tokens('%sFOO' % operator) 148 | self.assertEqual(2, len(tokens), tokens) 149 | self.assertEqual(Syntax(operator, 0, 2), tokens[0]) 150 | self.assertEqual(Name('FOO', 2, 5), tokens[1]) 151 | 152 | # 012345 153 | tokens = self.get_tokens('%s FOO' % operator) 154 | self.assertEqual(2, len(tokens), tokens) 155 | self.assertEqual(Syntax(operator, 0, 2), tokens[0]) 156 | self.assertEqual(Name('FOO', 3, 6), tokens[1]) 157 | 158 | def test_get_tokens_post_increment_operators(self): 159 | for operator in ('++', '--'): 160 | # 012345 161 | tokens = self.get_tokens('FOO%s' % operator) 162 | self.assertEqual(2, len(tokens), tokens) 163 | self.assertEqual(Name('FOO', 0, 3), tokens[0]) 164 | self.assertEqual(Syntax(operator, 3, 5), tokens[1]) 165 | 166 | # 012345 167 | tokens = self.get_tokens('FOO %s' % operator) 168 | self.assertEqual(2, len(tokens), tokens) 169 | self.assertEqual(Name('FOO', 0, 3), tokens[0]) 170 | self.assertEqual(Syntax(operator, 4, 6), tokens[1]) 171 | 172 | def test_get_tokens_semicolons(self): 173 | # 0123456 789012 174 | tokens = self.get_tokens(' foo;\n bar;') 175 | self.assertEqual(4, len(tokens), tokens) 176 | self.assertEqual(Name('foo', 2, 5), tokens[0]) 177 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 178 | self.assertEqual(Name('bar', 9, 12), tokens[2]) 179 | self.assertEqual(Syntax(';', 12, 13), tokens[3]) 180 | 181 | def test_get_tokens_pointers1(self): 182 | # 0123456789 183 | tokens = self.get_tokens('foo->bar;') 184 | self.assertEqual(4, len(tokens), tokens) 185 | self.assertEqual(Name('foo', 0, 3), tokens[0]) 186 | self.assertEqual(Syntax('->', 3, 5), tokens[1]) 187 | self.assertEqual(Name('bar', 5, 8), tokens[2]) 188 | self.assertEqual(Syntax(';', 8, 9), tokens[3]) 189 | 190 | def test_get_tokens_pointers2(self): 191 | # 01234567890 192 | tokens = self.get_tokens('(*foo).bar;') 193 | self.assertEqual(7, len(tokens), tokens) 194 | self.assertEqual(Syntax('(', 0, 1), tokens[0]) 195 | self.assertEqual(Syntax('*', 1, 2), tokens[1]) 196 | self.assertEqual(Name('foo', 2, 5), tokens[2]) 197 | self.assertEqual(Syntax(')', 5, 6), tokens[3]) 198 | self.assertEqual(Syntax('.', 6, 7), tokens[4]) 199 | self.assertEqual(Name('bar', 7, 10), tokens[5]) 200 | self.assertEqual(Syntax(';', 10, 11), tokens[6]) 201 | 202 | def test_get_tokens_block(self): 203 | # 0123456 204 | tokens = self.get_tokens('{ 0; }') 205 | self.assertEqual(4, len(tokens), tokens) 206 | self.assertEqual(Syntax('{', 0, 1), tokens[0]) 207 | self.assertEqual(Constant('0', 2, 3), tokens[1]) 208 | self.assertEqual(Syntax(';', 3, 4), tokens[2]) 209 | self.assertEqual(Syntax('}', 5, 6), tokens[3]) 210 | 211 | def test_get_tokens_bit_fields(self): 212 | # 012345678901234567 213 | tokens = self.get_tokens('unsigned foo : 1;') 214 | self.assertEqual(5, len(tokens), tokens) 215 | self.assertEqual(Name('unsigned', 0, 8), tokens[0]) 216 | self.assertEqual(Name('foo', 9, 12), tokens[1]) 217 | self.assertEqual(Syntax(':', 13, 14), tokens[2]) 218 | self.assertEqual(Constant('1', 15, 16), tokens[3]) 219 | self.assertEqual(Syntax(';', 16, 17), tokens[4]) 220 | 221 | def test_get_tokens_assignment(self): 222 | # 012345678901234567 223 | tokens = self.get_tokens('unsigned foo = 1;') 224 | self.assertEqual(5, len(tokens), tokens) 225 | self.assertEqual(Name('unsigned', 0, 8), tokens[0]) 226 | self.assertEqual(Name('foo', 9, 12), tokens[1]) 227 | self.assertEqual(Syntax('=', 13, 14), tokens[2]) 228 | self.assertEqual(Constant('1', 15, 16), tokens[3]) 229 | self.assertEqual(Syntax(';', 16, 17), tokens[4]) 230 | 231 | # 012345678901234 5678 232 | tokens = self.get_tokens('unsigned foo =\n 1;') 233 | self.assertEqual(5, len(tokens), tokens) 234 | self.assertEqual(Name('unsigned', 0, 8), tokens[0]) 235 | self.assertEqual(Name('foo', 9, 12), tokens[1]) 236 | self.assertEqual(Syntax('=', 13, 14), tokens[2]) 237 | self.assertEqual(Constant('1', 16, 17), tokens[3]) 238 | self.assertEqual(Syntax(';', 17, 18), tokens[4]) 239 | 240 | # 012345678901234 5 6789 241 | tokens = self.get_tokens('unsigned foo =\\\n 1;') 242 | self.assertEqual(5, len(tokens), tokens) 243 | self.assertEqual(Name('unsigned', 0, 8), tokens[0]) 244 | self.assertEqual(Name('foo', 9, 12), tokens[1]) 245 | self.assertEqual(Syntax('=', 13, 14), tokens[2]) 246 | self.assertEqual(Constant('1', 17, 18), tokens[3]) 247 | self.assertEqual(Syntax(';', 18, 19), tokens[4]) 248 | 249 | def test_get_tokens_int_constants(self): 250 | # 01234 251 | tokens = self.get_tokens('123;') 252 | self.assertEqual(2, len(tokens), tokens) 253 | self.assertEqual(Constant('123', 0, 3), tokens[0]) 254 | self.assertEqual(Syntax(';', 3, 4), tokens[1]) 255 | 256 | for suffix in ('l', 'u', 'ul', 'll', 'ull'): 257 | # 0123456 258 | tokens = self.get_tokens('123%s;' % suffix) 259 | self.assertEqual(2, len(tokens), tokens) 260 | value = '123%s' % suffix 261 | size = len(value) 262 | self.assertEqual(Constant(value, 0, size), tokens[0]) 263 | self.assertEqual(Syntax(';', size, size + 1), tokens[1]) 264 | 265 | suffix = suffix.upper() 266 | 267 | # 0123456 268 | tokens = self.get_tokens('123%s;' % suffix) 269 | self.assertEqual(2, len(tokens), tokens) 270 | value = '123%s' % suffix 271 | size = len(value) 272 | self.assertEqual(Constant(value, 0, size), tokens[0]) 273 | self.assertEqual(Syntax(';', size, size + 1), tokens[1]) 274 | 275 | def test_get_tokens_octal_constants(self): 276 | # 0123456789 277 | tokens = self.get_tokens('01234567;') 278 | self.assertEqual(2, len(tokens), tokens) 279 | self.assertEqual(Constant('01234567', 0, 8), tokens[0]) 280 | self.assertEqual(Syntax(';', 8, 9), tokens[1]) 281 | 282 | for suffix in ('l', 'u', 'ul', 'll', 'ull'): 283 | # 012345678901 284 | tokens = self.get_tokens('01234567%s;' % suffix) 285 | self.assertEqual(2, len(tokens), tokens) 286 | value = '01234567%s' % suffix 287 | size = len(value) 288 | self.assertEqual(Constant(value, 0, size), tokens[0]) 289 | self.assertEqual(Syntax(';', size, size + 1), tokens[1]) 290 | 291 | suffix = suffix.upper() 292 | 293 | # 012345678901 294 | tokens = self.get_tokens('01234567%s;' % suffix) 295 | self.assertEqual(2, len(tokens), tokens) 296 | value = '01234567%s' % suffix 297 | size = len(value) 298 | self.assertEqual(Constant(value, 0, size), tokens[0]) 299 | self.assertEqual(Syntax(';', size, size + 1), tokens[1]) 300 | 301 | def test_get_tokens_hex_constants(self): 302 | # 012345678901 303 | tokens = self.get_tokens('0xDeadBEEF;') 304 | self.assertEqual(2, len(tokens), tokens) 305 | self.assertEqual(Constant('0xDeadBEEF', 0, 10), tokens[0]) 306 | self.assertEqual(Syntax(';', 10, 11), tokens[1]) 307 | 308 | for suffix in ('l', 'u', 'ul', 'll', 'ull'): 309 | # 0123456789 310 | tokens = self.get_tokens('0xBEEF%s;' % suffix) 311 | self.assertEqual(2, len(tokens), tokens) 312 | value = '0xBEEF%s' % suffix 313 | size = len(value) 314 | self.assertEqual(Constant(value, 0, size), tokens[0]) 315 | self.assertEqual(Syntax(';', size, size + 1), tokens[1]) 316 | 317 | suffix = suffix.upper() 318 | 319 | # 0123456789 320 | tokens = self.get_tokens('0xBEEF%s;' % suffix) 321 | self.assertEqual(2, len(tokens), tokens) 322 | value = '0xBEEF%s' % suffix 323 | size = len(value) 324 | self.assertEqual(Constant(value, 0, size), tokens[0]) 325 | self.assertEqual(Syntax(';', size, size + 1), tokens[1]) 326 | 327 | def test_get_tokens_float_constants(self): 328 | # 012345678901 329 | tokens = self.get_tokens('3.14;') 330 | self.assertEqual(2, len(tokens), tokens) 331 | self.assertEqual(Constant('3.14', 0, 4), tokens[0]) 332 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 333 | 334 | # 012345678901 335 | tokens = self.get_tokens('3.14E;') 336 | self.assertEqual(2, len(tokens), tokens) 337 | self.assertEqual(Constant('3.14E', 0, 5), tokens[0]) 338 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 339 | 340 | # 012345678901 341 | tokens = self.get_tokens('3.14e;') 342 | self.assertEqual(2, len(tokens), tokens) 343 | self.assertEqual(Constant('3.14e', 0, 5), tokens[0]) 344 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 345 | 346 | # 012345678901 347 | tokens = self.get_tokens('.14;') 348 | self.assertEqual(2, len(tokens), tokens) 349 | self.assertEqual(Constant('.14', 0, 3), tokens[0]) 350 | self.assertEqual(Syntax(';', 3, 4), tokens[1]) 351 | 352 | # 012345678901 353 | tokens = self.get_tokens('3.14e+10;') 354 | self.assertEqual(2, len(tokens), tokens) 355 | self.assertEqual(Constant('3.14e+10', 0, 8), tokens[0]) 356 | self.assertEqual(Syntax(';', 8, 9), tokens[1]) 357 | 358 | # 012345678901 359 | tokens = self.get_tokens('3.14e-10;') 360 | self.assertEqual(2, len(tokens), tokens) 361 | self.assertEqual(Constant('3.14e-10', 0, 8), tokens[0]) 362 | self.assertEqual(Syntax(';', 8, 9), tokens[1]) 363 | 364 | # 012345678901 365 | tokens = self.get_tokens('3.14f;') 366 | self.assertEqual(2, len(tokens), tokens) 367 | self.assertEqual(Constant('3.14f', 0, 5), tokens[0]) 368 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 369 | 370 | # 012345678901 371 | tokens = self.get_tokens('3.14l;') 372 | self.assertEqual(2, len(tokens), tokens) 373 | self.assertEqual(Constant('3.14l', 0, 5), tokens[0]) 374 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 375 | 376 | # 012345678901 377 | tokens = self.get_tokens('3.14F;') 378 | self.assertEqual(2, len(tokens), tokens) 379 | self.assertEqual(Constant('3.14F', 0, 5), tokens[0]) 380 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 381 | 382 | # 012345678901 383 | tokens = self.get_tokens('3.14L;') 384 | self.assertEqual(2, len(tokens), tokens) 385 | self.assertEqual(Constant('3.14L', 0, 5), tokens[0]) 386 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 387 | 388 | # 012345678901 389 | tokens = self.get_tokens('.14f;') 390 | self.assertEqual(2, len(tokens), tokens) 391 | self.assertEqual(Constant('.14f', 0, 4), tokens[0]) 392 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 393 | 394 | # 012345678901 395 | tokens = self.get_tokens('.14l;') 396 | self.assertEqual(2, len(tokens), tokens) 397 | self.assertEqual(Constant('.14l', 0, 4), tokens[0]) 398 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 399 | 400 | # 012345678901 401 | tokens = self.get_tokens('.14F;') 402 | self.assertEqual(2, len(tokens), tokens) 403 | self.assertEqual(Constant('.14F', 0, 4), tokens[0]) 404 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 405 | 406 | # 012345678901 407 | tokens = self.get_tokens('.14L;') 408 | self.assertEqual(2, len(tokens), tokens) 409 | self.assertEqual(Constant('.14L', 0, 4), tokens[0]) 410 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 411 | 412 | def test_get_tokens_char_constants(self): 413 | # 012345678901 414 | tokens = self.get_tokens("'5';") 415 | self.assertEqual(2, len(tokens), tokens) 416 | self.assertEqual(Constant("'5'", 0, 3), tokens[0]) 417 | self.assertEqual(Syntax(';', 3, 4), tokens[1]) 418 | 419 | # 012345678901 420 | tokens = self.get_tokens("u'5';") 421 | self.assertEqual(2, len(tokens), tokens) 422 | self.assertEqual(Constant("u'5'", 0, 4), tokens[0]) 423 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 424 | 425 | # 012345678901 426 | tokens = self.get_tokens("U'5';") 427 | self.assertEqual(2, len(tokens), tokens) 428 | self.assertEqual(Constant("U'5'", 0, 4), tokens[0]) 429 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 430 | 431 | # 012345678901 432 | tokens = self.get_tokens("L'5';") 433 | self.assertEqual(2, len(tokens), tokens) 434 | self.assertEqual(Constant("L'5'", 0, 4), tokens[0]) 435 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 436 | 437 | # 012345678901 438 | tokens = self.get_tokens(r"'\005';") 439 | self.assertEqual(2, len(tokens), tokens) 440 | self.assertEqual(Constant(r"'\005'", 0, 6), tokens[0]) 441 | self.assertEqual(Syntax(';', 6, 7), tokens[1]) 442 | 443 | # 012345678901 444 | tokens = self.get_tokens(r"'\\';") 445 | self.assertEqual(2, len(tokens), tokens) 446 | self.assertEqual(Constant(r"'\\'", 0, 4), tokens[0]) 447 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 448 | 449 | # 01 2345678901 450 | tokens = self.get_tokens(r"'\'';") 451 | self.assertEqual(2, len(tokens), tokens) 452 | self.assertEqual(Constant(r"'\''", 0, 4), tokens[0]) 453 | self.assertEqual(Syntax(';', 4, 5), tokens[1]) 454 | 455 | # 01 2345678901 456 | tokens = self.get_tokens(r"U'\'';") 457 | self.assertEqual(2, len(tokens), tokens) 458 | self.assertEqual(Constant(r"U'\''", 0, 5), tokens[0]) 459 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 460 | 461 | def test_get_tokens_string_constants(self): 462 | # 0123456 463 | tokens = self.get_tokens('"str";') 464 | self.assertEqual(2, len(tokens), tokens) 465 | self.assertEqual(Constant('"str"', 0, 5), tokens[0]) 466 | self.assertEqual(Syntax(';', 5, 6), tokens[1]) 467 | 468 | # 01234567 469 | tokens = self.get_tokens('u"str";') 470 | self.assertEqual(2, len(tokens), tokens) 471 | self.assertEqual(Constant('u"str"', 0, 6), tokens[0]) 472 | self.assertEqual(Syntax(';', 6, 7), tokens[1]) 473 | 474 | # 01234567 475 | tokens = self.get_tokens('U"str";') 476 | self.assertEqual(2, len(tokens), tokens) 477 | self.assertEqual(Constant('U"str"', 0, 6), tokens[0]) 478 | self.assertEqual(Syntax(';', 6, 7), tokens[1]) 479 | 480 | # 012345678 481 | tokens = self.get_tokens('u8"str";') 482 | self.assertEqual(2, len(tokens), tokens) 483 | self.assertEqual(Constant('u8"str"', 0, 7), tokens[0]) 484 | self.assertEqual(Syntax(';', 7, 8), tokens[1]) 485 | 486 | # 01234567890 487 | tokens = self.get_tokens(r'"s\"t\"r";') 488 | self.assertEqual(2, len(tokens), tokens) 489 | self.assertEqual(Constant(r'"s\"t\"r"', 0, 9), tokens[0]) 490 | self.assertEqual(Syntax(';', 9, 10), tokens[1]) 491 | 492 | # 012345678 493 | tokens = self.get_tokens(r'"str\\";') 494 | self.assertEqual(2, len(tokens), tokens) 495 | self.assertEqual(Constant(r'"str\\"', 0, 7), tokens[0]) 496 | self.assertEqual(Syntax(';', 7, 8), tokens[1]) 497 | 498 | def test_get_tokens_ternary_operator(self): 499 | # 012345678901234567 500 | tokens = self.get_tokens('cond ? foo : bar;') 501 | self.assertEqual(6, len(tokens), tokens) 502 | self.assertEqual(Name('cond', 0, 4), tokens[0]) 503 | self.assertEqual(Syntax('?', 5, 6), tokens[1]) 504 | self.assertEqual(Name('foo', 7, 10), tokens[2]) 505 | self.assertEqual(Syntax(':', 11, 12), tokens[3]) 506 | self.assertEqual(Name('bar', 13, 16), tokens[4]) 507 | self.assertEqual(Syntax(';', 16, 17), tokens[5]) 508 | 509 | def test_get_tokens_identifier(self): 510 | # 0123456 511 | tokens = self.get_tokens('U elt;') 512 | self.assertEqual(3, len(tokens), tokens) 513 | self.assertEqual(Name('U', 0, 1), tokens[0]) 514 | self.assertEqual(Name('elt', 2, 5), tokens[1]) 515 | self.assertEqual(Syntax(';', 5, 6), tokens[2]) 516 | 517 | # TODO(nnorwitz): test all the following 518 | # Augmented assignments (lots) 519 | # [] 520 | # () and function calls 521 | # comma operator 522 | # identifiers (e.g., _). what to do about dollar signs? 523 | 524 | 525 | if __name__ == '__main__': 526 | unittest.main() 527 | -------------------------------------------------------------------------------- /cpp/find_warnings.py: -------------------------------------------------------------------------------- 1 | # Copyright 2007 Neal Norwitz 2 | # Portions Copyright 2007 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Find warnings for C++ code. 17 | 18 | TODO(nnorwitz): provide a mechanism to configure which warnings should 19 | be generated and which should be suppressed. Currently, all possible 20 | warnings will always be displayed. There is no way to suppress any. 21 | There also needs to be a way to use annotations in the source code to 22 | suppress warnings. 23 | """ 24 | 25 | from __future__ import absolute_import 26 | from __future__ import print_function 27 | from __future__ import unicode_literals 28 | 29 | import os 30 | import sys 31 | 32 | from . import ast 33 | from . import headers 34 | from . import keywords 35 | from . import metrics 36 | from . import symbols 37 | from . import tokenize 38 | from . import utils 39 | 40 | 41 | try: 42 | basestring 43 | except NameError: 44 | basestring = str 45 | 46 | 47 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 48 | 49 | 50 | HEADER_EXTENSIONS = frozenset(['.h', '.hh', '.hpp', '.h++', '.hxx', '.cuh']) 51 | CPP_EXTENSIONS = frozenset(['.cc', '.cpp', '.c++', '.cxx', '.cu']) 52 | 53 | # These enumerations are used to determine how a symbol/#include file is used. 54 | UNUSED = 0 55 | USES_REFERENCE = 1 56 | USES_DECLARATION = 2 57 | 58 | DECLARATION_TYPES = (ast.Class, ast.Struct, ast.Enum, ast.Union) 59 | 60 | 61 | class Module(object): 62 | 63 | """Data container representing a single source file.""" 64 | 65 | def __init__(self, filename, ast_list): 66 | self.filename = filename 67 | self.ast_list = ast_list 68 | self.public_symbols = self._get_exported_symbols() 69 | 70 | def _get_exported_symbols(self): 71 | if not self.ast_list: 72 | return {} 73 | return dict([(n.name, n) for n in self.ast_list if n.is_exportable()]) 74 | 75 | 76 | def is_header_file(filename): 77 | _, ext = os.path.splitext(filename) 78 | return ext.lower() in HEADER_EXTENSIONS 79 | 80 | 81 | def is_cpp_file(filename): 82 | _, ext = os.path.splitext(filename) 83 | return ext.lower() in CPP_EXTENSIONS 84 | 85 | 86 | class WarningHunter(object): 87 | 88 | # Cache filename: ast_list 89 | _module_cache = {} 90 | 91 | def __init__(self, filename, source, ast_list, include_paths, 92 | system_include_paths, nonsystem_include_paths, 93 | quiet=False): 94 | self.filename = filename 95 | self.source = source 96 | self.ast_list = ast_list 97 | self.include_paths = include_paths[:] 98 | self.system_include_paths = system_include_paths 99 | self.nonsystem_include_paths = nonsystem_include_paths 100 | self.quiet = quiet 101 | self.symbol_table = symbols.SymbolTable() 102 | 103 | self.metrics = metrics.Metrics(source) 104 | self.warnings = set() 105 | if filename not in self._module_cache: 106 | self._module_cache[filename] = Module(filename, ast_list) 107 | 108 | def _add_warning(self, msg, node, filename=None): 109 | if filename is not None: 110 | contents = utils.read_file(filename) 111 | src_metrics = metrics.Metrics(contents) 112 | else: 113 | filename = self.filename 114 | src_metrics = self.metrics 115 | line_number = get_line_number(src_metrics, node) 116 | self.warnings.add((filename, line_number, msg)) 117 | 118 | def show_warnings(self): 119 | for filename, line_num, msg in sorted(self.warnings): 120 | if line_num == 0: 121 | print('{}: {}'.format(filename, msg)) 122 | else: 123 | print('{}:{}: {}'.format(filename, line_num, msg)) 124 | 125 | def find_warnings(self): 126 | if is_header_file(self.filename): 127 | self._find_header_warnings() 128 | elif is_cpp_file(self.filename): 129 | self._find_source_warnings() 130 | 131 | def _update_symbol_table(self, module): 132 | for name, node in module.public_symbols.items(): 133 | self.symbol_table.add_symbol(name, node.namespace, node, module) 134 | 135 | def _get_module(self, node): 136 | include_paths = [os.path.dirname(self.filename)] + self.include_paths 137 | source, filename = headers.read_source(node.filename, include_paths) 138 | 139 | if source is None: 140 | module = Module(filename, None) 141 | msg = "unable to find '{}'".format(filename) 142 | self._add_warning(msg, node) 143 | elif filename in self._module_cache: 144 | # The cache survives across all instances, but the symbol table 145 | # is per instance, so we need to make sure the symbol table 146 | # is updated even if the module was in the cache. 147 | module = self._module_cache[filename] 148 | self._update_symbol_table(module) 149 | else: 150 | ast_list = None 151 | try: 152 | builder = ast.builder_from_source(source, filename, 153 | self.system_include_paths, 154 | self.nonsystem_include_paths, 155 | quiet=self.quiet) 156 | ast_list = [_f for _f in builder.generate() if _f] 157 | except tokenize.TokenError: 158 | pass 159 | except ast.ParseError as error: 160 | if not self.quiet: 161 | print( 162 | "Exception while processing '{}': {}".format( 163 | filename, 164 | error), 165 | file=sys.stderr) 166 | module = Module(filename, ast_list) 167 | self._module_cache[filename] = module 168 | self._update_symbol_table(module) 169 | return module 170 | 171 | def _read_and_parse_includes(self): 172 | # Map header-filename: (#include AST node, module). 173 | included_files = {} 174 | # Map declaration-name: AST node. 175 | forward_declarations = {} 176 | files_seen = {} 177 | for node in self.ast_list: 178 | if isinstance(node, ast.Include): 179 | if node.system: 180 | filename = node.filename 181 | else: 182 | module = self._get_module(node) 183 | filename = module.filename 184 | _, ext = os.path.splitext(filename) 185 | if ext.lower() != '.hxx': 186 | included_files[filename] = node, module 187 | if is_cpp_file(filename): 188 | self._add_warning( 189 | "should not #include C++ source file '{}'".format( 190 | node.filename), 191 | node) 192 | if filename == self.filename: 193 | self._add_warning( 194 | "'{}' #includes itself".format(node.filename), 195 | node) 196 | if filename in files_seen: 197 | include_node = files_seen[filename] 198 | line_num = get_line_number(self.metrics, include_node) 199 | self._add_warning( 200 | "'{}' already #included on line {}".format( 201 | node.filename, 202 | line_num), 203 | node) 204 | else: 205 | files_seen[filename] = node 206 | if isinstance(node, DECLARATION_TYPES) and node.is_declaration(): 207 | forward_declarations[node.full_name()] = node 208 | 209 | return included_files, forward_declarations 210 | 211 | def _verify_include_files_used(self, file_uses, included_files): 212 | """Find all #include files that are unnecessary.""" 213 | for include_file, use in file_uses.items(): 214 | if not use & USES_DECLARATION: 215 | node, module = included_files[include_file] 216 | if module.ast_list is not None: 217 | msg = "'{}' does not need to be #included".format( 218 | node.filename) 219 | if use & USES_REFERENCE: 220 | msg += '; use a forward declaration instead' 221 | self._add_warning(msg, node) 222 | 223 | def _verify_forward_declarations_used(self, forward_declarations, 224 | decl_uses, file_uses): 225 | """Find all the forward declarations that are not used.""" 226 | for cls in forward_declarations: 227 | if cls in file_uses: 228 | if not decl_uses[cls] & USES_DECLARATION: 229 | node = forward_declarations[cls] 230 | msg = ("'{}' forward declared, " 231 | 'but needs to be #included'.format(cls)) 232 | self._add_warning(msg, node) 233 | else: 234 | if decl_uses[cls] == UNUSED: 235 | node = forward_declarations[cls] 236 | msg = "'{}' not used".format(cls) 237 | self._add_warning(msg, node) 238 | 239 | def _determine_uses(self, included_files, forward_declarations): 240 | """Set up the use type of each symbol.""" 241 | file_uses = dict.fromkeys(included_files, UNUSED) 242 | decl_uses = dict.fromkeys(forward_declarations, UNUSED) 243 | symbol_table = self.symbol_table 244 | 245 | for name, node in forward_declarations.items(): 246 | try: 247 | symbol_table.lookup_symbol(node.name, node.namespace) 248 | decl_uses[name] |= USES_REFERENCE 249 | except symbols.Error: 250 | module = Module(name, None) 251 | symbol_table.add_symbol(node.name, node.namespace, node, 252 | module) 253 | 254 | def _do_lookup(name, namespace): 255 | try: 256 | file_use_node = symbol_table.lookup_symbol(name, namespace) 257 | except symbols.Error: 258 | return 259 | name = file_use_node[1].filename 260 | file_uses[name] = file_uses.get(name, 0) | USES_DECLARATION 261 | 262 | def _add_declaration(name, namespace): 263 | if not name: 264 | # Ignore anonymous struct. It is not standard, but we might as 265 | # well avoid crashing if it is easy. 266 | return 267 | 268 | names = [n for n in namespace if n is not None] 269 | if names: 270 | name = '::'.join(names) + '::' + name 271 | if name in decl_uses: 272 | decl_uses[name] |= USES_DECLARATION 273 | 274 | def _add_reference(name, namespace): 275 | try: 276 | file_use_node = symbol_table.lookup_symbol(name, namespace) 277 | except symbols.Error: 278 | return 279 | 280 | name = file_use_node[1].filename 281 | if file_use_node[1].ast_list is None: 282 | decl_uses[name] |= USES_REFERENCE 283 | elif name in file_uses: 284 | # enum and typedef can't be forward declared 285 | if isinstance(file_use_node[0], (ast.Enum, ast.Typedef)): 286 | file_uses[name] |= USES_DECLARATION 287 | else: 288 | file_uses[name] |= USES_REFERENCE 289 | 290 | def _add_use(node, namespace, name=''): 291 | if isinstance(node, basestring): 292 | name = node 293 | elif isinstance(node, list): 294 | # name contains a list of tokens. 295 | name = '::'.join([n.name for n in name]) 296 | 297 | # node is a Type so look for its symbol immediately. 298 | if name: 299 | _do_lookup(name, namespace) 300 | return 301 | 302 | # Try to search for the value of the variable declaration for any 303 | # symbols, such as `#define` values or other variable names which 304 | # may be included in other files. 305 | obj = getattr(node, 'initial_value', None) 306 | if obj: 307 | _do_lookup(obj, namespace) 308 | 309 | # If node is a VariableDeclaration, check if the variable type is 310 | # a symbol used in other includes. 311 | obj = getattr(node, 'type', None) 312 | if obj and isinstance(obj.name, basestring): 313 | _do_lookup(obj.name, namespace) 314 | 315 | if not isinstance(node, basestring): 316 | # Happens when variables are defined with inlined types, e.g.: 317 | # enum {...} variable; 318 | return 319 | 320 | def _add_variable(node, namespace, reference=False): 321 | obj = node.type if isinstance( 322 | node, ast.VariableDeclaration) else node 323 | 324 | if obj.reference or obj.pointer or reference: 325 | _add_reference(obj.name, namespace) 326 | else: 327 | # Add a use for the variable declaration type as well as the 328 | # variable value. 329 | _add_use(obj.name, namespace) 330 | _add_use(node, namespace) 331 | # This needs to recurse when the node is a templated type. 332 | _add_template_use(obj.name, 333 | obj.templated_types, 334 | namespace, 335 | reference) 336 | 337 | def _process_function(function, namespace): 338 | reference = function.body is None 339 | if function.return_type: 340 | return_type = function.return_type 341 | _add_variable(return_type, namespace, reference) 342 | 343 | for s in function.specializations: 344 | _add_variable(s, namespace, not function.body) 345 | 346 | templated_types = function.templated_types or () 347 | for p in function.parameters: 348 | node = p.type 349 | if node.name not in templated_types: 350 | if function.body and p.name: 351 | # Assume that if the function has a body and a name 352 | # the parameter type is really used. 353 | # NOTE(nnorwitz): this is over-aggressive. It would be 354 | # better to iterate through the body and determine 355 | # actual uses based on local vars and data members 356 | # used. 357 | _add_use(node.name, namespace) 358 | elif ( 359 | p.default and 360 | p.default[0].name != '0' and 361 | p.default[0].name != 'NULL' and 362 | p.default[0].name != 'nullptr' 363 | ): 364 | _add_use(node.name, namespace) 365 | elif node.reference or node.pointer or reference: 366 | _add_reference(node.name, namespace) 367 | else: 368 | _add_use(node.name, namespace) 369 | _add_template_use(node.name, 370 | node.templated_types, 371 | namespace, 372 | reference) 373 | 374 | def _process_function_body(function, namespace): 375 | previous = None 376 | save = namespace[:] 377 | for t in function.body: 378 | if t.token_type == tokenize.NAME: 379 | previous = t 380 | if not keywords.is_keyword(t.name): 381 | # TODO(nnorwitz): handle static function calls. 382 | # TODO(nnorwitz): handle using statements in file. 383 | # TODO(nnorwitz): handle using statements in function. 384 | # TODO(nnorwitz): handle namespace assignment in file. 385 | _add_use(t.name, namespace) 386 | elif t.name == '::' and previous is not None: 387 | namespace.append(previous.name) 388 | elif t.name in (':', ';'): 389 | namespace = save[:] 390 | 391 | def _add_template_use(name, types, namespace, reference=False): 392 | for cls in types or (): 393 | if cls.pointer or cls.reference or reference: 394 | _add_reference(cls.name, namespace) 395 | elif name.endswith('_ptr'): 396 | # Special case templated classes that end w/_ptr. 397 | # These are things like auto_ptr which do 398 | # not require the class definition, only decl. 399 | _add_reference(cls.name, namespace) 400 | elif name.startswith('Q') and name.endswith('Pointer'): 401 | # Special case templated classes from the Qt framework. 402 | _add_reference(cls.name, namespace) 403 | else: 404 | _add_use(cls.name, namespace) 405 | _add_template_use(cls.name, cls.templated_types, 406 | namespace, reference) 407 | 408 | def _process_types(nodes, namespace): 409 | for node in nodes: 410 | if isinstance(node, ast.Type): 411 | _add_variable(node, namespace) 412 | 413 | # Iterate through the source AST/tokens, marking each symbols use. 414 | ast_seq = [self.ast_list] 415 | namespace_stack = [] 416 | while ast_seq: 417 | for node in ast_seq.pop(): 418 | if isinstance(node, ast.VariableDeclaration): 419 | namespace = namespace_stack + node.namespace 420 | _add_variable(node, namespace) 421 | elif isinstance(node, ast.Function): 422 | namespace = namespace_stack + node.namespace 423 | _process_function(node, namespace) 424 | if node.body: 425 | _process_function_body(node, namespace) 426 | elif isinstance(node, ast.Typedef): 427 | namespace = namespace_stack + node.namespace 428 | _process_types(node.alias, namespace) 429 | elif isinstance(node, ast.Friend): 430 | expr = node.expr 431 | namespace = namespace_stack + node.namespace 432 | if isinstance(expr, ast.Type): 433 | _add_reference(expr.name, namespace) 434 | elif isinstance(expr, ast.Function): 435 | _process_function(expr, namespace) 436 | elif isinstance(node, ast.Union) and node.body is not None: 437 | ast_seq.append(node.body) 438 | elif isinstance(node, ast.Class) and node.body is not None: 439 | _add_declaration(node.name, node.namespace) 440 | namespace = namespace_stack + node.namespace 441 | _add_template_use('', node.bases, namespace) 442 | ast_seq.append(node.body) 443 | elif isinstance(node, ast.Using): 444 | if node.names[0].name == 'namespace': 445 | namespace_stack.append(node.names[1].name) 446 | 447 | return file_uses, decl_uses 448 | 449 | def _find_unused_warnings(self, included_files, forward_declarations, 450 | primary_header=None): 451 | file_uses, decl_uses = self._determine_uses(included_files, 452 | forward_declarations) 453 | if primary_header and primary_header.filename in file_uses: 454 | file_uses[primary_header.filename] |= USES_DECLARATION 455 | self._verify_include_files_used(file_uses, included_files) 456 | self._verify_forward_declarations_used(forward_declarations, decl_uses, 457 | file_uses) 458 | for node in forward_declarations.values(): 459 | try: 460 | file_use_node = self.symbol_table.lookup_symbol(node.name, 461 | node.namespace) 462 | except symbols.Error: 463 | continue 464 | name = file_use_node[1].filename 465 | if ( 466 | file_use_node[1].ast_list is not None and 467 | name in file_uses and 468 | file_uses[name] & USES_DECLARATION 469 | ): 470 | msg = ("'{}' forward declared, " 471 | "but already #included in '{}'".format(node.name, name)) 472 | self._add_warning(msg, node) 473 | 474 | def _find_incorrect_case(self, included_files): 475 | for (filename, node_and_module) in included_files.items(): 476 | base_name = os.path.basename(filename) 477 | try: 478 | candidates = os.listdir(os.path.dirname(filename)) 479 | except OSError: 480 | continue 481 | 482 | correct_filename = get_correct_include_filename(base_name, 483 | candidates) 484 | if correct_filename: 485 | self._add_warning( 486 | "'{}' should be '{}'".format(base_name, correct_filename), 487 | node_and_module[0]) 488 | 489 | def _find_header_warnings(self): 490 | included_files, forward_declarations = self._read_and_parse_includes() 491 | self._find_unused_warnings(included_files, forward_declarations) 492 | self._find_incorrect_case(included_files) 493 | 494 | def _find_public_function_warnings(self, node, name, primary_header, 495 | all_headers): 496 | # Not found in the primary header, search all other headers. 497 | for _, header in all_headers.values(): 498 | if name in header.public_symbols: 499 | # If the primary.filename == header.filename, it probably 500 | # indicates an error elsewhere. It sucks to mask it, 501 | # but false positives are worse. 502 | if primary_header: 503 | msg = ("expected to find '{}' in '{}', " 504 | "but found in '{}'".format(name, 505 | primary_header.filename, 506 | header.filename)) 507 | self._add_warning(msg, node) 508 | break 509 | else: 510 | where = 'in any directly #included header' 511 | if primary_header: 512 | where = ( 513 | "in expected header '{}' " 514 | 'or any other directly #included header'.format( 515 | primary_header.filename)) 516 | 517 | if name != 'main' and name != name.upper(): 518 | self._add_warning("'{}' not found {}".format(name, where), 519 | node) 520 | 521 | def _check_public_functions(self, primary_header, all_headers): 522 | """Verify all the public functions are also declared in a header 523 | file.""" 524 | public_symbols = {} 525 | declared_only_symbols = {} 526 | if primary_header: 527 | for name, symbol in primary_header.public_symbols.items(): 528 | if isinstance(symbol, ast.Function): 529 | public_symbols[name] = symbol 530 | declared_only_symbols = dict.fromkeys(public_symbols, True) 531 | 532 | for node in self.ast_list: 533 | # Make sure we have a function that should be exported. 534 | if not isinstance(node, ast.Function): 535 | continue 536 | if isinstance(node, ast.Method): 537 | # Ensure that for Foo::Bar, Foo is *not* a namespace. 538 | # If Foo is a namespace, we have a function and not a method. 539 | names = [n.name for n in node.in_class] 540 | if names != self.symbol_table.get_namespace(names): 541 | continue 542 | if not (node.is_definition() and node.is_exportable()): 543 | continue 544 | 545 | # This function should be declared in a header file. 546 | name = node.name 547 | if name in public_symbols: 548 | declared_only_symbols[name] = False 549 | else: 550 | self._find_public_function_warnings(node, 551 | name, 552 | primary_header, 553 | all_headers) 554 | 555 | for name, declared_only in declared_only_symbols.items(): 556 | if declared_only: 557 | node = public_symbols[name] 558 | if node.templated_types is None: 559 | msg = "'{}' declared but not defined".format(name) 560 | self._add_warning(msg, node, primary_header.filename) 561 | 562 | def _get_primary_header(self, included_files): 563 | basename = os.path.basename(os.path.splitext(self.filename)[0]) 564 | include_paths = [os.path.dirname(self.filename)] + self.include_paths 565 | source, filename = headers.read_source(basename + '.h', include_paths) 566 | primary_header = included_files.get(filename) 567 | if primary_header: 568 | return primary_header[1] 569 | if source is not None: 570 | msg = "should #include header file '{}'".format(filename) 571 | self.warnings.add((self.filename, 0, msg)) 572 | return None 573 | 574 | def _find_source_warnings(self): 575 | included_files, forward_declarations = self._read_and_parse_includes() 576 | self._find_incorrect_case(included_files) 577 | 578 | for node in forward_declarations.values(): 579 | # TODO(nnorwitz): This really isn't a problem, but might 580 | # be something to warn against. I expect this will either 581 | # be configurable or removed in the future. But it's easy 582 | # to check for now. 583 | msg = ( 584 | "'{}' forward declaration not expected in source file".format( 585 | node.name)) 586 | self._add_warning(msg, node) 587 | 588 | # A primary header is optional. However, when looking up 589 | # defined methods in the source, always look in the 590 | # primary_header first. Expect that is the most likely location. 591 | # Use of primary_header is primarily an optimization. 592 | primary_header = self._get_primary_header(included_files) 593 | 594 | self._check_public_functions(primary_header, included_files) 595 | if primary_header and primary_header.ast_list is not None: 596 | includes = [ 597 | node.filename 598 | for node in primary_header.ast_list 599 | if isinstance(node, ast.Include) 600 | ] 601 | for (node, _) in included_files.values(): 602 | if node.filename in includes: 603 | msg = "'{}' already #included in '{}'".format( 604 | node.filename, primary_header.filename) 605 | self._add_warning(msg, node) 606 | 607 | # TODO(nnorwitz): other warnings to add: 608 | # * unused forward decls for variables (globals)/classes 609 | # * Functions that are too large/complex 610 | # * Variables declared far from first use 611 | # * primitive member variables not initialized in ctor 612 | 613 | 614 | def get_line_number(metrics_instance, node): 615 | return metrics_instance.get_line_number(node.start) 616 | 617 | 618 | def get_correct_include_filename(filename, candidate_filenames): 619 | if filename not in candidate_filenames: 620 | for candidate in candidate_filenames: 621 | if filename.lower() == candidate.lower(): 622 | return candidate 623 | return None 624 | 625 | 626 | def run(filename, source, entire_ast, include_paths, 627 | system_include_paths, nonsystem_include_paths, quiet): 628 | hunter = WarningHunter(filename, source, entire_ast, 629 | include_paths=include_paths, 630 | system_include_paths=system_include_paths, 631 | nonsystem_include_paths=nonsystem_include_paths, 632 | quiet=quiet) 633 | hunter.find_warnings() 634 | hunter.show_warnings() 635 | return len(hunter.warnings) 636 | -------------------------------------------------------------------------------- /test_ast.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2008 Neal Norwitz 4 | # Portions Copyright 2008 Google Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | """AST test.""" 19 | 20 | from __future__ import absolute_import 21 | 22 | import unittest 23 | import os 24 | 25 | from cpp import ast 26 | from cpp import tokenize 27 | 28 | 29 | __author__ = 'nnorwitz@google.com (Neal Norwitz)' 30 | 31 | 32 | def _install_generic_equal(cls, attrs): 33 | """Add an __eq__ method to |cls| so objects can be compared for tests. 34 | 35 | Args: 36 | cls: Python class to add __eq__ method to 37 | attrs: string - space separated of attribute names to compare 38 | """ 39 | attrs = attrs.split() 40 | 41 | def __eq__(self, other): 42 | if not isinstance(other, cls): 43 | return False 44 | for a in attrs: 45 | # Use not (a == other) since this could be recursive and 46 | # we don't define a not equals method. 47 | if not (getattr(self, a) == getattr(other, a)): 48 | return False 49 | return True 50 | cls.__eq__ = __eq__ 51 | 52 | 53 | def _install_equal_methods(): 54 | """Install __eq__ methods on the appropriate objects used for testing.""" 55 | _install_generic_equal(tokenize.Token, 'name') 56 | _install_generic_equal(ast.Class, 57 | 'name bases templated_types namespace body') 58 | _install_generic_equal(ast.Struct, 59 | 'name bases templated_types namespace body') 60 | _install_generic_equal(ast.Type, ('name templated_types modifiers ' 61 | 'reference pointer array')) 62 | _install_generic_equal(ast.Parameter, 'name type default') 63 | _install_generic_equal(ast.Function, ('name return_type parameters ' 64 | 'specializations modifiers ' 65 | 'templated_types body namespace')) 66 | _install_generic_equal(ast.Method, ('name in_class return_type parameters ' 67 | 'specializations modifiers ' 68 | 'templated_types body namespace')) 69 | _install_generic_equal(ast.Define, 'name definition') 70 | _install_generic_equal(ast.Include, 'filename system') 71 | _install_generic_equal(ast.Typedef, 'name alias namespace') 72 | _install_generic_equal(ast.VariableDeclaration, 73 | 'name type initial_value namespace') 74 | 75 | 76 | _install_equal_methods() 77 | 78 | 79 | def get_tokens(code_string): 80 | return tokenize.get_tokens(code_string + '\n') 81 | 82 | 83 | def MakeBuilder(code_string): 84 | """Convenience function to make an ASTBuilder from a code snippet..""" 85 | return ast.ASTBuilder(get_tokens(code_string), '') 86 | 87 | 88 | def Token(name, start=0, end=0, token_type=tokenize.NAME): 89 | return tokenize.Token(token_type, name, start, end) 90 | 91 | 92 | def Define(name, definition, start=0, end=0): 93 | return ast.Define(start, end, name, definition) 94 | 95 | 96 | def Include(filename, system=False, start=0, end=0): 97 | return ast.Include(start, end, filename, system) 98 | 99 | 100 | def Class(name, start=0, end=0, bases=None, body=None, templated_types=None, 101 | namespace=None): 102 | if namespace is None: 103 | namespace = [] 104 | 105 | return ast.Class(start, end, name, bases, templated_types, body, namespace) 106 | 107 | 108 | def Struct(name, start=0, end=0, bases=None, body=None, templated_types=None, 109 | namespace=None): 110 | if namespace is None: 111 | namespace = [] 112 | 113 | return ast.Struct(start, end, name, bases, templated_types, body, 114 | namespace) 115 | 116 | 117 | def Type(name, start=0, end=0, templated_types=None, modifiers=None, 118 | reference=False, pointer=False, array=False): 119 | if templated_types is None: 120 | templated_types = [] 121 | 122 | if modifiers is None: 123 | modifiers = [] 124 | 125 | return ast.Type(start, end, name, templated_types, modifiers, 126 | reference, pointer, array) 127 | 128 | 129 | def Function(name, return_type, parameters, start=0, end=0, 130 | specializations=None, modifiers=0, templated_types=None, 131 | body=None, namespace=None): 132 | if specializations is None: 133 | specializations = [] 134 | if namespace is None: 135 | namespace = [] 136 | 137 | return ast.Function(start, end, name, return_type, parameters, 138 | specializations, modifiers, templated_types, 139 | body, namespace) 140 | 141 | 142 | def Method(name, in_class, return_type, parameters, start=0, end=0, 143 | specializations=None, modifiers=0, templated_types=None, 144 | body=None, namespace=None): 145 | if specializations is None: 146 | specializations = [] 147 | if namespace is None: 148 | namespace = [] 149 | 150 | return ast.Method(start, end, name, in_class, return_type, parameters, 151 | specializations, modifiers, templated_types, 152 | body, namespace) 153 | 154 | 155 | def Typedef(name, start=0, end=0, alias=None, namespace=None): 156 | if namespace is None: 157 | namespace = [] 158 | 159 | return ast.Typedef(start, end, name, alias, namespace) 160 | 161 | 162 | def VariableDeclaration(name, var_type, start=0, end=0, initial_value='', 163 | namespace=None): 164 | if namespace is None: 165 | namespace = [] 166 | 167 | return ast.VariableDeclaration(start, end, name, var_type, initial_value, 168 | namespace) 169 | 170 | 171 | class CoverageTest(unittest.TestCase): 172 | 173 | def test_coverage(self): 174 | self.assertFalse(Type('Foo') == Type('Bar')) 175 | self.assertFalse(Type('Foo') == Typedef('Foo')) 176 | 177 | 178 | class TypeConverterDeclarationToPartsTest(unittest.TestCase): 179 | 180 | def setUp(self): 181 | self.converter = ast.TypeConverter([]) 182 | 183 | def test_simple(self): 184 | tokens = get_tokens('Fool data') 185 | name, type_name, templated_types, modifiers, _, __ = \ 186 | self.converter.declaration_to_parts(list(tokens), True) 187 | self.assertEqual('data', name) 188 | self.assertEqual('Fool', type_name) 189 | self.assertEqual([], templated_types) 190 | self.assertEqual([], modifiers) 191 | 192 | def test_simple_modifiers(self): 193 | tokens = get_tokens('const volatile Fool data') 194 | name, type_name, templated_types, modifiers, _, __ = \ 195 | self.converter.declaration_to_parts(list(tokens), True) 196 | self.assertEqual('data', name) 197 | self.assertEqual('Fool', type_name) 198 | self.assertEqual([], templated_types) 199 | self.assertEqual(['const', 'volatile'], modifiers) 200 | 201 | def test_simple_array(self): 202 | tokens = get_tokens('Fool[] data') 203 | name, type_name, templated_types, modifiers, _, __ = \ 204 | self.converter.declaration_to_parts(list(tokens), True) 205 | self.assertEqual('data', name) 206 | self.assertEqual('Fool', type_name) 207 | self.assertEqual([], templated_types) 208 | self.assertEqual([], modifiers) 209 | 210 | def test_simple_template(self): 211 | tokens = get_tokens('Fool data') 212 | name, type_name, templated_types, modifiers, _, __ = \ 213 | self.converter.declaration_to_parts(list(tokens), True) 214 | self.assertEqual('data', name) 215 | self.assertEqual('Fool', type_name) 216 | self.assertEqual([Type('tt')], templated_types) 217 | self.assertEqual([], modifiers) 218 | 219 | 220 | class TypeConverterToParametersTest(unittest.TestCase): 221 | 222 | def setUp(self): 223 | self.converter = ast.TypeConverter([]) 224 | 225 | def test_really_simple(self): 226 | tokens = get_tokens('int bar') 227 | results = self.converter.to_parameters(list(tokens)) 228 | self.assertEqual(1, len(results), repr(results)) 229 | 230 | self.assertEqual([], results[0].type.modifiers) 231 | self.assertEqual('int', results[0].type.name) 232 | self.assertEqual([], results[0].type.templated_types) 233 | self.assertEqual(False, results[0].type.pointer) 234 | self.assertEqual(False, results[0].type.reference) 235 | self.assertEqual(False, results[0].type.array) 236 | self.assertEqual('bar', results[0].name) 237 | 238 | def test_array(self): 239 | tokens = get_tokens('int[] bar') 240 | results = self.converter.to_parameters(list(tokens)) 241 | self.assertEqual(1, len(results), repr(results)) 242 | 243 | self.assertEqual([], results[0].type.modifiers) 244 | self.assertEqual('int', results[0].type.name) 245 | self.assertEqual([], results[0].type.templated_types) 246 | self.assertEqual(True, results[0].type.pointer) 247 | self.assertEqual(False, results[0].type.reference) 248 | self.assertEqual(False, results[0].type.array) 249 | self.assertEqual('bar', results[0].name) 250 | 251 | def test_array_pointer_reference(self): 252 | params = 'const int[] bar, mutable char* foo, volatile Bar& babar' 253 | tokens = get_tokens(params) 254 | results = self.converter.to_parameters(list(tokens)) 255 | self.assertEqual(3, len(results), repr(results)) 256 | 257 | self.assertEqual(['const'], results[0].type.modifiers) 258 | self.assertEqual('int', results[0].type.name) 259 | self.assertEqual([], results[0].type.templated_types) 260 | self.assertEqual(True, results[0].type.pointer) 261 | self.assertEqual(False, results[0].type.reference) 262 | self.assertEqual(False, results[0].type.array) 263 | self.assertEqual('bar', results[0].name) 264 | 265 | self.assertEqual(['mutable'], results[1].type.modifiers) 266 | self.assertEqual('char', results[1].type.name) 267 | self.assertEqual([], results[1].type.templated_types) 268 | self.assertEqual(True, results[1].type.pointer) 269 | self.assertEqual(False, results[1].type.reference) 270 | self.assertEqual(False, results[1].type.array) 271 | self.assertEqual('foo', results[1].name) 272 | 273 | self.assertEqual(['volatile'], results[2].type.modifiers) 274 | self.assertEqual('Bar', results[2].type.name) 275 | self.assertEqual([], results[2].type.templated_types) 276 | self.assertEqual(False, results[2].type.pointer) 277 | self.assertEqual(True, results[2].type.reference) 278 | self.assertEqual(False, results[2].type.array) 279 | self.assertEqual('babar', results[2].name) 280 | 281 | def test_array_with_class(self): 282 | tokens = get_tokens('Bar[] bar') 283 | results = self.converter.to_parameters(list(tokens)) 284 | self.assertEqual(1, len(results), repr(results)) 285 | 286 | self.assertEqual([], results[0].type.modifiers) 287 | self.assertEqual('Bar', results[0].type.name) 288 | self.assertEqual([], results[0].type.templated_types) 289 | self.assertEqual(True, results[0].type.pointer) 290 | self.assertEqual(False, results[0].type.reference) 291 | self.assertEqual(False, results[0].type.array) 292 | self.assertEqual('bar', results[0].name) 293 | 294 | def test_multiple_args(self): 295 | tokens = get_tokens('const volatile Fool* data, int bar, enum X foo') 296 | results = self.converter.to_parameters(list(tokens)) 297 | self.assertEqual(3, len(results), repr(results)) 298 | 299 | self.assertEqual(['const', 'volatile'], results[0].type.modifiers) 300 | self.assertEqual('Fool', results[0].type.name) 301 | self.assertEqual([], results[0].type.templated_types) 302 | self.assertEqual(True, results[0].type.pointer) 303 | self.assertEqual(False, results[0].type.reference) 304 | self.assertEqual('data', results[0].name) 305 | 306 | self.assertEqual([], results[1].type.modifiers) 307 | self.assertEqual('int', results[1].type.name) 308 | self.assertEqual([], results[1].type.templated_types) 309 | self.assertEqual(False, results[1].type.pointer) 310 | self.assertEqual(False, results[1].type.reference) 311 | self.assertEqual('bar', results[1].name) 312 | 313 | self.assertEqual(['enum'], results[2].type.modifiers) 314 | self.assertEqual('X', results[2].type.name) 315 | self.assertEqual([], results[2].type.templated_types) 316 | self.assertEqual(False, results[2].type.pointer) 317 | self.assertEqual(False, results[2].type.reference) 318 | self.assertEqual('foo', results[2].name) 319 | 320 | def test_simple_template_begin(self): 321 | tokens = get_tokens('pair data, int bar') 322 | results = self.converter.to_parameters(list(tokens)) 323 | self.assertEqual(2, len(results), repr(results)) 324 | 325 | self.assertEqual([], results[0].type.modifiers) 326 | self.assertEqual('pair', results[0].type.name) 327 | self.assertEqual([Type('int'), Type('int')], 328 | results[0].type.templated_types) 329 | self.assertEqual(False, results[0].type.pointer) 330 | self.assertEqual(False, results[0].type.reference) 331 | self.assertEqual('data', results[0].name) 332 | 333 | self.assertEqual([], results[1].type.modifiers) 334 | self.assertEqual('int', results[1].type.name) 335 | self.assertEqual([], results[1].type.templated_types) 336 | self.assertEqual(False, results[1].type.pointer) 337 | self.assertEqual(False, results[1].type.reference) 338 | self.assertEqual('bar', results[1].name) 339 | 340 | def test_nested_template(self): 341 | tokens = get_tokens('vector> data') 342 | results = self.converter.to_parameters(list(tokens)) 343 | self.assertEqual(1, len(results), repr(results)) 344 | 345 | self.assertEqual([], results[0].type.modifiers) 346 | self.assertEqual('vector', results[0].type.name) 347 | self.assertEqual('data', results[0].name) 348 | 349 | results = results[0].type.templated_types 350 | self.assertEqual(1, len(results), repr(results)) 351 | self.assertEqual('pair', results[0].name) 352 | 353 | results = results[0].templated_types 354 | self.assertEqual(2, len(results), repr(results)) 355 | self.assertEqual('int', results[0].name) 356 | self.assertEqual('float', results[1].name) 357 | 358 | def test_simple_with_initializers(self): 359 | tokens = get_tokens('Fool* data = NULL') 360 | results = self.converter.to_parameters(list(tokens)) 361 | self.assertEqual(1, len(results), repr(results)) 362 | 363 | self.assertEqual([], results[0].type.modifiers) 364 | self.assertEqual('Fool', results[0].type.name) 365 | self.assertEqual([], results[0].type.templated_types) 366 | self.assertEqual(True, results[0].type.pointer) 367 | self.assertEqual(False, results[0].type.reference) 368 | self.assertEqual(False, results[0].type.array) 369 | self.assertEqual('data', results[0].name) 370 | self.assertEqual([Token('NULL')], results[0].default) 371 | 372 | def test_templated_default_value(self): 373 | tokens = get_tokens('int i = Handle()') 374 | results = self.converter.to_parameters(list(tokens)) 375 | self.assertEqual(1, len(results), repr(results)) 376 | 377 | self.assertEqual([], results[0].type.modifiers) 378 | self.assertEqual('int', results[0].type.name) 379 | self.assertEqual([], results[0].type.templated_types) 380 | self.assertEqual(False, results[0].type.pointer) 381 | self.assertEqual(False, results[0].type.reference) 382 | self.assertEqual(False, results[0].type.array) 383 | self.assertEqual('i', results[0].name) 384 | 385 | def test_complex_default_value(self): 386 | tokens = get_tokens('int i = 4 * 2)') 387 | results = self.converter.to_parameters(list(tokens)) 388 | self.assertEqual(1, len(results), repr(results)) 389 | 390 | self.assertEqual([], results[0].type.modifiers) 391 | self.assertEqual('int', results[0].type.name) 392 | self.assertEqual([], results[0].type.templated_types) 393 | self.assertEqual(False, results[0].type.pointer) 394 | self.assertEqual(False, results[0].type.reference) 395 | self.assertEqual(False, results[0].type.array) 396 | self.assertEqual('i', results[0].name) 397 | 398 | 399 | class TypeConverterToTypeTest(unittest.TestCase): 400 | 401 | def setUp(self): 402 | self.converter = ast.TypeConverter([]) 403 | 404 | def test_simple(self): 405 | tokens = get_tokens('Bar') 406 | result = self.converter.to_type(list(tokens)) 407 | self.assertEqual(1, len(result)) 408 | self.assertEqual(Type('Bar'), result[0]) 409 | 410 | def test_template(self): 411 | tokens = get_tokens('Bar') 412 | result = self.converter.to_type(list(tokens)) 413 | self.assertEqual(1, len(result)) 414 | self.assertEqual(Type('Bar', templated_types=[Type('Foo')]), 415 | result[0]) 416 | 417 | def test_templated_type(self): 418 | tokens = get_tokens('Registry::listener') 419 | result = self.converter.to_type(list(tokens)) 420 | self.assertEqual(1, len(result)) 421 | types = [Type('T')] 422 | self.assertEqual(Type('Registry::listener', templated_types=types), 423 | result[0]) 424 | 425 | def test_template_with_multiple_args(self): 426 | tokens = get_tokens('Bar') 427 | result = self.converter.to_type(list(tokens)) 428 | self.assertEqual(1, len(result)) 429 | types = [Type('Foo'), Type('Blah'), Type('Bling')] 430 | self.assertEqual(Type('Bar', templated_types=types), result[0]) 431 | 432 | def test_template_with_multiple_template_args_start(self): 433 | tokens = get_tokens('Bar, Blah, Bling>') 434 | result = self.converter.to_type(list(tokens)) 435 | self.assertEqual(1, len(result)) 436 | types = [Type('Foo', templated_types=[Type('x')]), 437 | Type('Blah'), 438 | Type('Bling')] 439 | self.assertEqual(types[0], result[0].templated_types[0]) 440 | self.assertEqual(types[1], result[0].templated_types[1]) 441 | self.assertEqual(types[2], result[0].templated_types[2]) 442 | self.assertEqual(Type('Bar', templated_types=types), result[0]) 443 | 444 | def test_template_with_multiple_template_args_mid(self): 445 | tokens = get_tokens('Bar, Bling>') 446 | result = self.converter.to_type(list(tokens)) 447 | self.assertEqual(1, len(result)) 448 | types = [Type('Foo'), 449 | Type('Blah', templated_types=[Type('x')]), 450 | Type('Bling')] 451 | self.assertEqual(Type('Bar', templated_types=types), result[0]) 452 | 453 | def test_template_with_multiple_template_args_end(self): 454 | tokens = get_tokens('Bar >') 455 | result = self.converter.to_type(list(tokens)) 456 | self.assertEqual(1, len(result)) 457 | types = [Type('Foo'), 458 | Type('Blah'), 459 | Type('Bling', templated_types=[Type('x')])] 460 | self.assertEqual(Type('Bar', templated_types=types), result[0]) 461 | 462 | def test_template_with_multiple_template_args_reference(self): 463 | tokens = get_tokens('Foo&, int>') 464 | result = self.converter.to_type(list(tokens)) 465 | self.assertEqual(1, len(result)) 466 | types = [Type('Bar', reference=True, templated_types=[Type('int')]), 467 | Type('int')] 468 | self.assertEqual(Type('Foo', templated_types=types), result[0]) 469 | 470 | def test_template_with_multiple_template_args_pointer(self): 471 | tokens = get_tokens('Foo*, int>') 472 | result = self.converter.to_type(list(tokens)) 473 | self.assertEqual(1, len(result)) 474 | types = [Type('Bar', pointer=True, templated_types=[Type('int')]), 475 | Type('int')] 476 | self.assertEqual(Type('Foo', templated_types=types), result[0]) 477 | 478 | def test_template_with_function_arg_zero_arg(self): 479 | tokens = get_tokens('function') 480 | result = self.converter.to_type(list(tokens)) 481 | self.assertEqual(1, len(result)) 482 | types = [Type('void')] 483 | self.assertEqual(Type('function', templated_types=types), result[0]) 484 | 485 | def test_template_with_function_arg_one_arg(self): 486 | tokens = get_tokens('function') 487 | result = self.converter.to_type(list(tokens)) 488 | self.assertEqual(1, len(result)) 489 | types = [Type('void'), 490 | Type('int')] 491 | self.assertEqual(Type('function', templated_types=types), result[0]) 492 | 493 | def test_template_with_function_arg_two_args(self): 494 | tokens = get_tokens('function') 495 | result = self.converter.to_type(list(tokens)) 496 | self.assertEqual(1, len(result)) 497 | types = [Type('void'), 498 | Type('int'), 499 | Type('int')] 500 | self.assertEqual(Type('function', templated_types=types), result[0]) 501 | 502 | def test_template_with_function_arg_and_nested_template(self): 503 | tokens = get_tokens('function&, int)>') 504 | result = self.converter.to_type(list(tokens)) 505 | self.assertEqual(1, len(result)) 506 | types = [Type('void'), 507 | Type('vector', reference=True, templated_types=[Type('int')]), 508 | Type('int')] 509 | self.assertEqual(Type('function', templated_types=types), result[0]) 510 | 511 | def test_array(self): 512 | tokens = get_tokens('Foo[]') 513 | result = self.converter.to_type(list(tokens)) 514 | self.assertEqual(1, len(result)) 515 | self.assertEqual(Type('Foo', pointer=True), result[0]) 516 | 517 | def test_array_with_size(self): 518 | tokens = get_tokens('Foo[42]') 519 | result = self.converter.to_type(list(tokens)) 520 | self.assertEqual(1, len(result)) 521 | self.assertEqual(Type('Foo', array=True), result[0]) 522 | 523 | 524 | class TypeConverterCreateReturnTypeTest(unittest.TestCase): 525 | 526 | def setUp(self): 527 | self.converter = ast.TypeConverter([]) 528 | 529 | def test_empty(self): 530 | self.assertEqual(None, self.converter.create_return_type(None)) 531 | self.assertEqual(None, self.converter.create_return_type([])) 532 | 533 | def test_simple(self): 534 | tokens = get_tokens('Bar') 535 | result = self.converter.create_return_type(list(tokens)) 536 | self.assertEqual(Type('Bar'), result) 537 | 538 | def test_array(self): 539 | tokens = get_tokens('Bar[]') 540 | result = self.converter.create_return_type(list(tokens)) 541 | self.assertEqual(Type('Bar', array=True), result) 542 | 543 | def test_const_pointer(self): 544 | tokens = get_tokens('const Bar*') 545 | result = self.converter.create_return_type(list(tokens)) 546 | self.assertEqual(Type('Bar', modifiers=['const'], pointer=True), 547 | result) 548 | 549 | def test_const_class_pointer(self): 550 | tokens = get_tokens('const class Bar*') 551 | result = self.converter.create_return_type(list(tokens)) 552 | modifiers = ['const', 'class'] 553 | self.assertEqual(Type('Bar', modifiers=modifiers, pointer=True), 554 | result) 555 | 556 | def test_template(self): 557 | tokens = get_tokens('const pair*') 558 | result = self.converter.create_return_type(list(tokens)) 559 | templated_types = [Type('int'), Type('NS::Foo')] 560 | self.assertEqual(Type('pair', modifiers=['const'], 561 | templated_types=templated_types, pointer=True), 562 | result) 563 | 564 | 565 | class ASTBuilderGetTemplatedTypesTest(unittest.TestCase): 566 | 567 | def test_simple(self): 568 | builder = MakeBuilder('T> class') 569 | result = builder._get_templated_types() 570 | self.assertEqual(1, len(result)) 571 | self.assertEqual((None, None), result['T']) 572 | 573 | def test_multiple(self): 574 | builder = MakeBuilder('T, U> class') 575 | result = builder._get_templated_types() 576 | self.assertEqual(2, len(result)) 577 | self.assertEqual((None, None), result['T']) 578 | self.assertEqual((None, None), result['U']) 579 | 580 | def test_multiple_with_typename(self): 581 | builder = MakeBuilder('typename T, typename U> class') 582 | result = builder._get_templated_types() 583 | self.assertEqual(2, len(result)) 584 | self.assertEqual((None, None), result['T']) 585 | self.assertEqual((None, None), result['U']) 586 | 587 | def test_multiple_with_typename_and_defaults(self): 588 | builder = MakeBuilder('typename T=XX, typename U=YY> class') 589 | result = builder._get_templated_types() 590 | self.assertEqual(2, len(result)) 591 | self.assertEqual(None, result['T'][0]) 592 | self.assertEqual(1, len(result['T'][1])) 593 | self.assertEqual('XX', result['T'][1][0].name) 594 | self.assertEqual(None, result['U'][0]) 595 | self.assertEqual(1, len(result['U'][1])) 596 | self.assertEqual('YY', result['U'][1][0].name) 597 | 598 | def test_multiple_with_user_defined_type_name(self): 599 | builder = MakeBuilder('class C, Type t> class') 600 | result = builder._get_templated_types() 601 | self.assertEqual(2, len(result)) 602 | self.assertEqual((None, None), result['C']) 603 | self.assertEqual('Type', result['t'][0].name) 604 | 605 | 606 | class ASTBuilderIntegrationTest(unittest.TestCase): 607 | 608 | """Unlike the other test cases in this file, this test case is meant to be 609 | an integration test. 610 | 611 | It doesn't test any individual method. It tests whole code blocks. 612 | """ 613 | 614 | def test_variable_array(self): 615 | nodes = list(MakeBuilder('int value[42];').generate()) 616 | self.assertEqual(1, len(nodes), repr(nodes)) 617 | self.assertEqual(VariableDeclaration('value', Type('int', array=True)), 618 | nodes[0]) 619 | 620 | def test_variable_initialization_with_initializer_list(self): 621 | nodes = list(MakeBuilder('int value = {42};').generate()) 622 | self.assertEqual(1, len(nodes), repr(nodes)) 623 | self.assertEqual(VariableDeclaration('value', Type('int'), 624 | initial_value='{42}'), 625 | nodes[0]) 626 | 627 | def test_variable_initialization_with_initializer_list2(self): 628 | nodes = list(MakeBuilder('auto il = {10,20,30};').generate()) 629 | self.assertEqual(1, len(nodes), repr(nodes)) 630 | self.assertEqual(VariableDeclaration('il', Type('auto'), 631 | initial_value='{10,20,30}'), 632 | nodes[0]) 633 | 634 | def test_variable_initialization_with_function(self): 635 | nodes = list(MakeBuilder('int value = fct();').generate()) 636 | self.assertEqual(1, len(nodes), repr(nodes)) 637 | self.assertEqual(VariableDeclaration('value', Type('int'), 638 | initial_value='fct()'), 639 | nodes[0]) 640 | 641 | def test_variable_initialization_with_complex_expression(self): 642 | nodes = list(MakeBuilder('int value = fct() + 42;').generate()) 643 | self.assertEqual(1, len(nodes), repr(nodes)) 644 | self.assertEqual(VariableDeclaration('value', Type('int'), 645 | initial_value='fct()+42'), 646 | nodes[0]) 647 | 648 | def test_variable_function_reference(self): 649 | nodes = list(MakeBuilder('int* (&fn)();').generate()) 650 | self.assertEqual(1, len(nodes), repr(nodes)) 651 | self.assertEqual(VariableDeclaration('fn', Type('int', pointer=True)), 652 | nodes[0]) 653 | 654 | def test_variable_function_pointer_init(self): 655 | nodes = list(MakeBuilder('int (*fn)() = 0;').generate()) 656 | self.assertEqual(1, len(nodes), repr(nodes)) 657 | self.assertEqual(VariableDeclaration('fn', Type('int'), 658 | initial_value='0'), 659 | nodes[0]) 660 | 661 | def test_variable_anonymous_class(self): 662 | nodes = list(MakeBuilder('class {public:} a;').generate()) 663 | self.assertEqual(1, len(nodes), repr(nodes)) 664 | self.assertEqual(VariableDeclaration('a', Type(Class(None, body=[]))), 665 | nodes[0]) 666 | 667 | def test_variable_anonymous_class2(self): 668 | nodes = list(MakeBuilder('const class {public:} a;').generate()) 669 | self.assertEqual(1, len(nodes), repr(nodes)) 670 | self.assertEqual(VariableDeclaration('a', Type(Class(None, body=[]), 671 | modifiers=['const'])), 672 | nodes[0]) 673 | 674 | def test_function_one_argument_with_name(self): 675 | for argument in ('Foo f', 'const Foo f', 'Foo& f', 'const Foo& f', 676 | 'unsigned int f', 'ns::foo f', 'std::vector f', 677 | 'const Foo* const f', 'auto f'): 678 | code = 'void fct(%s);' % argument 679 | nodes = list(MakeBuilder(code).generate()) 680 | self.assertEqual(1, len(nodes), repr(nodes)) 681 | self.assertEqual(1, len(nodes[0].parameters)) 682 | self.assertEqual('f', nodes[0].parameters[0].name) 683 | 684 | def test_function_one_argument_with_no_name(self): 685 | for argument in ('Foo', 'const Foo', 'Foo&', 'const Foo&', 686 | 'unsigned int', 'ns::foo', 'std::vector', 687 | 'const Foo* const', 'auto int'): 688 | code = 'void fct(%s);' % argument 689 | nodes = list(MakeBuilder(code).generate()) 690 | self.assertEqual(1, len(nodes), repr(nodes)) 691 | self.assertEqual(1, len(nodes[0].parameters)) 692 | self.assertEqual(None, nodes[0].parameters[0].name) 693 | 694 | def test_function_return(self): 695 | for argument in ('class', 'enum', 'struct', 'union'): 696 | code = '%s A fn();' % argument 697 | nodes = list(MakeBuilder(code).generate()) 698 | self.assertEqual(1, len(nodes), repr(nodes)) 699 | self.assertEqual(Function('fn', list(get_tokens('A')), []), 700 | nodes[0]) 701 | 702 | def test_no_argument(self): 703 | nodes = list(MakeBuilder('FOO();').generate()) 704 | self.assertEqual(1, len(nodes), repr(nodes)) 705 | self.assertEqual(Function('FOO', [], []), nodes[0]) 706 | 707 | def test_one_argument(self): 708 | nodes = list(MakeBuilder('FOO(1);').generate()) 709 | self.assertEqual(1, len(nodes), repr(nodes)) 710 | self.assertEqual(Function('FOO', [], list(get_tokens('1'))), nodes[0]) 711 | 712 | def test_two_arguments(self): 713 | nodes = list(MakeBuilder('FOO(1,0);').generate()) 714 | self.assertEqual(1, len(nodes), repr(nodes)) 715 | self.assertEqual( 716 | Function('FOO', [], list(get_tokens('1,0'))), nodes[0]) 717 | 718 | def test_two_arguments_first_empty(self): 719 | nodes = list(MakeBuilder('FOO( ,0);').generate()) 720 | self.assertEqual(1, len(nodes), repr(nodes)) 721 | self.assertEqual(Function('FOO', [], list(get_tokens('0'))), nodes[0]) 722 | 723 | def test_two_arguments_second_empty(self): 724 | nodes = list(MakeBuilder('FOO(1, );').generate()) 725 | self.assertEqual(1, len(nodes), repr(nodes)) 726 | self.assertEqual(Function('FOO', [], list(get_tokens('1'))), nodes[0]) 727 | 728 | def test_two_arguments_both_empty(self): 729 | nodes = list(MakeBuilder('FOO( , );').generate()) 730 | self.assertEqual(1, len(nodes), repr(nodes)) 731 | self.assertEqual(Function('FOO', [], []), nodes[0]) 732 | 733 | def test_class_variable_declaration(self): 734 | nodes = list(MakeBuilder('class Foo foo;').generate()) 735 | self.assertEqual(1, len(nodes), repr(nodes)) 736 | self.assertEqual( 737 | VariableDeclaration('foo', Type('Foo')), nodes[0]) 738 | 739 | def test_struct_variable_declaration(self): 740 | nodes = list(MakeBuilder('struct Foo foo;').generate()) 741 | self.assertEqual(1, len(nodes), repr(nodes)) 742 | self.assertEqual( 743 | VariableDeclaration('foo', Type('Foo')), nodes[0]) 744 | 745 | def test_typedef(self): 746 | for argument in ('bool', 'char', 'int', 'long', 'short', 'double', 747 | 'float', 'void', 'wchar_t', 'unsigned', 'signed'): 748 | code = 'typedef %s Type;' % argument 749 | nodes = list(MakeBuilder(code).generate()) 750 | self.assertEqual(1, len(nodes), repr(nodes)) 751 | self.assertEqual(Typedef('Type', alias=[Type('%s' % argument)]), 752 | nodes[0]) 753 | 754 | def test_anon_class_typedef(self): 755 | nodes = list(MakeBuilder('typedef class { int zz; } Anon;').generate()) 756 | self.assertEqual(1, len(nodes), repr(nodes)) 757 | self.assertEqual( 758 | Typedef('Anon', 759 | alias=[Class(None, 760 | body=[VariableDeclaration('zz', 761 | Type('int'))])]), 762 | nodes[0]) 763 | 764 | def test_anon_struct_typedef(self): 765 | nodes = list( 766 | MakeBuilder('typedef struct { int zz; } Anon;').generate()) 767 | self.assertEqual(1, len(nodes), repr(nodes)) 768 | self.assertEqual( 769 | Typedef('Anon', 770 | alias=[Struct(None, 771 | body=[VariableDeclaration('zz', 772 | Type('int'))])]), 773 | nodes[0]) 774 | 775 | def test_class_typedef(self): 776 | nodes = list( 777 | MakeBuilder('typedef class _IplImage IplImage;').generate()) 778 | self.assertEqual(1, len(nodes), repr(nodes)) 779 | self.assertEqual(Typedef('IplImage', alias=[Class('_IplImage')]), 780 | nodes[0]) 781 | 782 | def test_struct_typedef(self): 783 | nodes = list( 784 | MakeBuilder('typedef struct _IplImage IplImage;').generate()) 785 | self.assertEqual(1, len(nodes), repr(nodes)) 786 | self.assertEqual(Typedef('IplImage', alias=[Struct('_IplImage')]), 787 | nodes[0]) 788 | 789 | def test_class_pointer_typedef(self): 790 | nodes = list( 791 | MakeBuilder('typedef class _IplImage *IplImage;').generate()) 792 | self.assertEqual(1, len(nodes), repr(nodes)) 793 | self.assertEqual(Typedef('IplImage', alias=[Class('_IplImage')]), 794 | nodes[0]) 795 | 796 | def test_struct_pointer_typedef(self): 797 | nodes = list( 798 | MakeBuilder('typedef struct _IplImage *IplImage;').generate()) 799 | self.assertEqual(1, len(nodes), repr(nodes)) 800 | self.assertEqual(Typedef('IplImage', alias=[Struct('_IplImage')]), 801 | nodes[0]) 802 | 803 | def test_class_forward_declaration(self): 804 | nodes = list(MakeBuilder('class Foo;').generate()) 805 | self.assertEqual(1, len(nodes), repr(nodes)) 806 | self.assertEqual(Class('Foo', body=None), nodes[0]) 807 | 808 | def test_struct_forward_declaration(self): 809 | nodes = list(MakeBuilder('struct Foo;').generate()) 810 | self.assertEqual(1, len(nodes), repr(nodes)) 811 | self.assertEqual(Struct('Foo', body=None), nodes[0]) 812 | 813 | def test_class_empty_body(self): 814 | nodes = list(MakeBuilder('class Foo {};').generate()) 815 | self.assertEqual(1, len(nodes), repr(nodes)) 816 | self.assertEqual(Class('Foo', body=[]), nodes[0]) 817 | 818 | def test_struct_empty_body(self): 819 | nodes = list(MakeBuilder('struct Foo {};').generate()) 820 | self.assertEqual(1, len(nodes), repr(nodes)) 821 | self.assertEqual(Struct('Foo', body=[]), nodes[0]) 822 | 823 | def test_class_final(self): 824 | nodes = list(MakeBuilder('class Foo final {};').generate()) 825 | self.assertEqual(1, len(nodes), repr(nodes)) 826 | self.assertEqual(Class('Foo', body=[]), nodes[0]) 827 | 828 | def test_class_exported(self): 829 | nodes = list(MakeBuilder('class DLLEXPORT Foo {};').generate()) 830 | self.assertEqual(1, len(nodes), repr(nodes)) 831 | self.assertEqual(Class('Foo', body=[]), nodes[0]) 832 | 833 | def test_struct_exported(self): 834 | nodes = list(MakeBuilder('struct DLLEXPORT Foo {};').generate()) 835 | self.assertEqual(1, len(nodes), repr(nodes)) 836 | self.assertEqual(Struct('Foo', body=[]), nodes[0]) 837 | 838 | def test_class_in_namespace_single(self): 839 | nodes = list(MakeBuilder('namespace N { class Foo; }').generate()) 840 | self.assertEqual(1, len(nodes), repr(nodes)) 841 | self.assertEqual(Class('Foo', namespace=['N']), nodes[0]) 842 | 843 | def test_class_in_namespace_multiple(self): 844 | code = 'namespace A { namespace B { namespace C { class Foo; }}}' 845 | nodes = list(MakeBuilder(code).generate()) 846 | self.assertEqual(1, len(nodes), repr(nodes)) 847 | self.assertEqual(Class('Foo', namespace=['A', 'B', 'C']), nodes[0]) 848 | 849 | def test_class_in_namespace_multiple_with_one_closed(self): 850 | code = 'namespace A { namespace B {} namespace C { class Foo; }}' 851 | nodes = list(MakeBuilder(code).generate()) 852 | self.assertEqual(1, len(nodes), repr(nodes)) 853 | self.assertEqual(Class('Foo', namespace=['A', 'C']), nodes[0]) 854 | 855 | def test_class_in_anonymous_namespace_single(self): 856 | nodes = list(MakeBuilder('namespace { class Foo; }').generate()) 857 | self.assertEqual(1, len(nodes), repr(nodes)) 858 | self.assertEqual(Class('Foo', namespace=[None]), nodes[0]) 859 | 860 | def test_class_in_anonymous_namespace_multiple(self): 861 | code = 'namespace A { namespace { namespace B { class Foo; }}}' 862 | nodes = list(MakeBuilder(code).generate()) 863 | self.assertEqual(1, len(nodes), repr(nodes)) 864 | self.assertEqual(Class('Foo', namespace=['A', None, 'B']), nodes[0]) 865 | 866 | def test_template_typedef(self): 867 | code = 'class Foo; typedef Bar v;' 868 | nodes = list(MakeBuilder(code).generate()) 869 | self.assertEqual(2, len(nodes), repr(nodes)) 870 | self.assertEqual(Class('Foo'), nodes[0]) 871 | self.assertEqual( 872 | Typedef('v', 873 | alias=[Type('Bar', 874 | templated_types=[Type('Foo', pointer=True)])]), 875 | nodes[1]) 876 | 877 | def test_operators(self): 878 | for operator in ('=', '+=', '-=', '*=', '==', '!=', '()', '[]', '<', 879 | '>', '^=', '<<=', '>>='): 880 | code = 'void Foo::operator%s();' % operator 881 | nodes = list(MakeBuilder(code).generate()) 882 | self.assertEqual(1, len(nodes), repr(nodes)) 883 | self.assertEqual(Method(('operator%s' % operator), 884 | list(get_tokens('Foo')), 885 | list(get_tokens('void')), []), nodes[0]) 886 | 887 | def test_class_virtual_inheritance(self): 888 | code = 'class Foo : public virtual Bar {};' 889 | nodes = list(MakeBuilder(code).generate()) 890 | self.assertEqual(1, len(nodes), repr(nodes)) 891 | self.assertEqual(Class('Foo', bases=[Type('Bar')], body=[]), nodes[0]) 892 | 893 | def test_class_virtual_inheritance_reverse(self): 894 | code = 'class Foo : virtual public Bar {};' 895 | nodes = list(MakeBuilder(code).generate()) 896 | self.assertEqual(1, len(nodes), repr(nodes)) 897 | self.assertEqual(Class('Foo', bases=[Type('Bar')], body=[]), nodes[0]) 898 | 899 | def test_class_macro_final(self): 900 | code = 'class EXPORT_MACRO Foo final {};' 901 | nodes = list(MakeBuilder(code).generate()) 902 | self.assertEqual(1, len(nodes), repr(nodes)) 903 | self.assertEqual(Class('Foo', body=[]), nodes[0]) 904 | 905 | def test_constructor(self): 906 | code = 'Foo::Foo() {}' 907 | nodes = list(MakeBuilder(code).generate()) 908 | self.assertEqual(1, len(nodes), repr(nodes)) 909 | self.assertEqual(Method('Foo', list(get_tokens('Foo')), [], [], 910 | body=[]), 911 | nodes[0]) 912 | 913 | def test_destructor(self): 914 | code = 'Foo::~Foo() {}' 915 | nodes = list(MakeBuilder(code).generate()) 916 | self.assertEqual(1, len(nodes), repr(nodes)) 917 | self.assertEqual(Method('~Foo', list(get_tokens('Foo')), [], [], 918 | body=[]), 919 | nodes[0]) 920 | 921 | def test_class_operators(self): 922 | for operator in ('=', '+=', '-=', '*=', '==', '!=', '()', '[]', '<', 923 | '>'): 924 | code = 'class Foo { void operator%s(); };' % operator 925 | nodes = list(MakeBuilder(code).generate()) 926 | self.assertEqual(1, len(nodes), repr(nodes)) 927 | function = nodes[0].body[0] 928 | expected = Function(('operator%s' % operator), 929 | list(get_tokens('void')), []) 930 | self.assertEqual(expected.return_type, function.return_type) 931 | self.assertEqual(expected, function) 932 | self.assertEqual(Class('Foo', body=[expected]), nodes[0]) 933 | 934 | def test_class_virtual_inline_destructor(self): 935 | code = 'class Foo { virtual inline ~Foo(); };' 936 | nodes = list(MakeBuilder(code).generate()) 937 | self.assertEqual(1, len(nodes), repr(nodes)) 938 | function = nodes[0].body[0] 939 | expected = Function('Foo', [], [], 940 | modifiers=ast.FUNCTION_DTOR | ast.FUNCTION_VIRTUAL) 941 | self.assertEqual(expected.return_type, function.return_type) 942 | self.assertEqual(expected, function) 943 | self.assertEqual(Class('Foo', body=[expected]), nodes[0]) 944 | 945 | def test_class_colon_separated_class_name_and_inline_dtor(self): 946 | method_body = 'XXX(1) << "should work";' 947 | code = 'class Foo::Bar { ~Bar() { %s } };' % method_body 948 | nodes = list(MakeBuilder(code).generate()) 949 | self.assertEqual(1, len(nodes), repr(nodes)) 950 | function = nodes[0].body[0] 951 | expected = Function('Bar', [], [], body=list(get_tokens(method_body)), 952 | modifiers=ast.FUNCTION_DTOR) 953 | self.assertEqual(expected.return_type, function.return_type) 954 | self.assertEqual(expected, function) 955 | self.assertEqual(Class('Foo::Bar', body=[expected]), nodes[0]) 956 | 957 | def test_class_handles_struct_rebind(self): 958 | code = """ 959 | template > 960 | class AnotherAllocator : public Alloc { 961 | template struct rebind { 962 | }; 963 | }; 964 | """ 965 | types1 = {} 966 | types1['Alloc'] = (None, list(get_tokens('std::allocator'))) 967 | types1['T'] = (None, None) 968 | types2 = {} 969 | types2['U'] = (None, None) 970 | 971 | nodes = list(MakeBuilder(code).generate()) 972 | self.assertEqual(1, len(nodes), repr(nodes)) 973 | self.assertEqual(Class('AnotherAllocator', bases=[Type('Alloc')], 974 | body=[Struct('rebind', body=[], 975 | templated_types=types2)], 976 | templated_types=types1,), 977 | nodes[0]) 978 | 979 | def test_class_ctor_initializer_list(self): 980 | code = """ 981 | class Foo { 982 | public: 983 | Foo() : 984 | arg1(1), 985 | arg2(2), 986 | arg3(3) 987 | {} 988 | private: 989 | int arg1; 990 | int arg2; 991 | int arg3; 992 | }; 993 | """ 994 | nodes = list(MakeBuilder(code).generate()) 995 | ctor = nodes[0].body[0] 996 | arg1 = nodes[0].body[1] 997 | arg2 = nodes[0].body[2] 998 | arg3 = nodes[0].body[3] 999 | 1000 | exp_ctor = Function( 1001 | 'Foo', [], [], modifiers=ast.FUNCTION_CTOR, body=[]) 1002 | exp_var = [VariableDeclaration('arg1', Type('int'), initial_value='1'), 1003 | VariableDeclaration('arg2', Type('int'), initial_value='2'), 1004 | VariableDeclaration('arg3', Type('int'), initial_value='3')] 1005 | 1006 | self.assertEqual(exp_ctor.return_type, ctor.return_type) 1007 | self.assertEqual(exp_ctor, ctor) 1008 | self.assertEqual(exp_var, [arg1, arg2, arg3]) 1009 | self.assertEqual(Class('Foo', body=[exp_ctor] + exp_var), nodes[0]) 1010 | 1011 | def test_class_ctor_initializer_list_cpp11(self): 1012 | code = """ 1013 | class Foo { 1014 | public: 1015 | Foo() : 1016 | arg1{1}, 1017 | arg2{2}, 1018 | arg3{3} 1019 | {} 1020 | private: 1021 | int arg1; 1022 | int arg2; 1023 | int arg3; 1024 | }; 1025 | """ 1026 | nodes = list(MakeBuilder(code).generate()) 1027 | ctor = nodes[0].body[0] 1028 | arg1 = nodes[0].body[1] 1029 | arg2 = nodes[0].body[2] 1030 | arg3 = nodes[0].body[3] 1031 | 1032 | exp_ctor = Function( 1033 | 'Foo', [], [], modifiers=ast.FUNCTION_CTOR, body=[]) 1034 | exp_var = [VariableDeclaration('arg1', Type('int'), initial_value='1'), 1035 | VariableDeclaration('arg2', Type('int'), initial_value='2'), 1036 | VariableDeclaration('arg3', Type('int'), initial_value='3')] 1037 | 1038 | self.assertEqual(exp_ctor.return_type, ctor.return_type) 1039 | self.assertEqual(exp_ctor, ctor) 1040 | self.assertEqual(exp_var, [arg1, arg2, arg3]) 1041 | self.assertEqual(Class('Foo', body=[exp_ctor] + exp_var), nodes[0]) 1042 | 1043 | def test_function_parses_operator_bracket(self): 1044 | code = """ 1045 | class A { 1046 | const B& operator[](const int i) const {} 1047 | }; 1048 | """ 1049 | nodes = list(MakeBuilder(code).generate()) 1050 | self.assertEqual(1, len(nodes), repr(nodes)) 1051 | function = nodes[0].body[0] 1052 | expected = Function('operator[]', list(get_tokens('const B&')), 1053 | list(get_tokens('const int i')), body=[], 1054 | modifiers=ast.FUNCTION_SPECIFIER) 1055 | self.assertEqual(expected.return_type, function.return_type) 1056 | self.assertEqual(expected, function) 1057 | self.assertEqual(Class('A', body=[expected]), nodes[0]) 1058 | 1059 | def test_function_parses_template_with_array_access(self): 1060 | code = """ 1061 | template 1062 | char (&ASH(T (&seq)[N]))[N]; 1063 | """ 1064 | nodes = list(MakeBuilder(code).generate()) 1065 | self.assertEqual(1, len(nodes), repr(nodes)) 1066 | # TODO(nnorwitz): this doesn't parse correctly, but at least 1067 | # it doesn't raise an exception anymore. Improve the parsing. 1068 | 1069 | def test_method_with_template_class_works(self): 1070 | code = """ 1071 | template 1072 | inline void EVM::VH::Write() { 1073 | } 1074 | """ 1075 | nodes = list(MakeBuilder(code).generate()) 1076 | self.assertEqual(1, len(nodes), repr(nodes)) 1077 | expected = Method('Write', list(get_tokens('EVM::VH')), 1078 | list(get_tokens('inline void')), [], 1079 | templated_types={'T': (None, None)}, 1080 | body=[]) 1081 | self.assertEqual(expected.return_type, nodes[0].return_type) 1082 | self.assertEqual(expected.in_class, nodes[0].in_class) 1083 | self.assertEqual(expected.templated_types, nodes[0].templated_types) 1084 | self.assertEqual(expected, nodes[0]) 1085 | 1086 | def test_method_with_template_class_with2args_works(self): 1087 | code = """ 1088 | template 1089 | inline void EVM::VH::Write() { 1090 | } 1091 | """ 1092 | nodes = list(MakeBuilder(code).generate()) 1093 | self.assertEqual(1, len(nodes), repr(nodes)) 1094 | expected = Method('Write', list(get_tokens('EVM::VH')), 1095 | list(get_tokens('inline void')), [], 1096 | templated_types={'T': (None, None), 1097 | 'U': (None, None)}, 1098 | body=[]) 1099 | self.assertEqual(expected.return_type, nodes[0].return_type) 1100 | self.assertEqual(expected.in_class, nodes[0].in_class) 1101 | self.assertEqual(expected.templated_types, nodes[0].templated_types) 1102 | self.assertEqual(expected, nodes[0]) 1103 | 1104 | def test_method_with_template_class_with3args_works(self): 1105 | code = """ 1106 | template 1107 | DT* Worker::Create() { 1108 | } 1109 | """ 1110 | nodes = list(MakeBuilder(code).generate()) 1111 | self.assertEqual(1, len(nodes), repr(nodes)) 1112 | tt = (None, None) 1113 | expected = Method('Create', list(get_tokens('Worker')), 1114 | list(get_tokens('DT*')), [], 1115 | templated_types={'CT': tt, 'IT': tt, 'DT': tt}, 1116 | body=[]) 1117 | self.assertEqual(expected.return_type, nodes[0].return_type) 1118 | self.assertEqual(expected.in_class, nodes[0].in_class) 1119 | self.assertEqual(expected.templated_types, nodes[0].templated_types) 1120 | self.assertEqual(expected, nodes[0]) 1121 | 1122 | def test_include_with_backslash_continuation_works(self): 1123 | nodes = list(MakeBuilder('#include \\\n "test.h"').generate()) 1124 | self.assertEqual(1, len(nodes), repr(nodes)) 1125 | self.assertEqual(Include('test.h'), nodes[0]) 1126 | 1127 | def test_system_include(self): 1128 | nodes = list(MakeBuilder('#include ').generate()) 1129 | self.assertEqual(1, len(nodes), repr(nodes)) 1130 | self.assertEqual(Include('vector', system=True), nodes[0]) 1131 | 1132 | def test_include_path_overrides(self): 1133 | paths = [os.path.dirname(os.path.realpath(__file__))] 1134 | fname = 'test/include.h' 1135 | 1136 | def _tokens(): 1137 | tokens_quotes = get_tokens('#include "test/include.h"') 1138 | tokens_brackets = get_tokens('#include ') 1139 | return [tokens_quotes, tokens_brackets] 1140 | 1141 | def _do_test(tokens, system_includes, nonsystem_includes, is_system): 1142 | nodes = list(ast.ASTBuilder( 1143 | tokens, '', system_includes=system_includes, 1144 | nonsystem_includes=nonsystem_includes).generate()) 1145 | self.assertEqual(1, len(nodes), repr(nodes)) 1146 | self.assertEqual(Include(fname, system=is_system), nodes[0]) 1147 | 1148 | # forcing system 1149 | for tokens in _tokens(): 1150 | _do_test(tokens, paths, [], True) 1151 | 1152 | # forcing nonsystem 1153 | for tokens in _tokens(): 1154 | _do_test(tokens, [], paths, False) 1155 | 1156 | # let the system infer 1157 | tokens_quotes, tokens_brackets = _tokens() 1158 | _do_test(tokens_quotes, [], [], False) 1159 | _do_test(tokens_brackets, [], [], True) 1160 | 1161 | def test_operator_new_bracket(self): 1162 | nodes = list( 1163 | MakeBuilder('void* operator new[](std::size_t size);').generate()) 1164 | self.assertEqual(1, len(nodes), repr(nodes)) 1165 | expected = Function('new[]', list(get_tokens('void* operator')), 1166 | list(get_tokens('std::size_t size'))) 1167 | self.assertEqual(expected, nodes[0]) 1168 | 1169 | def test_operator_delete_bracket(self): 1170 | nodes = list( 1171 | MakeBuilder('void operator delete[](void* ptr);').generate()) 1172 | self.assertEqual(1, len(nodes), repr(nodes)) 1173 | expected = Function('delete[]', list(get_tokens('void operator')), 1174 | list(get_tokens('void* ptr'))) 1175 | self.assertEqual(expected, nodes[0]) 1176 | 1177 | def test_define(self): 1178 | nodes = list(MakeBuilder('#define FOO 42').generate()) 1179 | self.assertEqual(1, len(nodes), repr(nodes)) 1180 | self.assertEqual(Define('FOO', '42'), nodes[0]) 1181 | 1182 | def test_define_with_backslash_continuation(self): 1183 | nodes = list(MakeBuilder('#define \\\n FOO 42').generate()) 1184 | self.assertEqual(1, len(nodes), repr(nodes)) 1185 | self.assertEqual(Define('FOO', '42'), nodes[0]) 1186 | 1187 | def test_define_with_backslash_continuation_between_declaration(self): 1188 | nodes = list(MakeBuilder('#define FOO \\\n 42').generate()) 1189 | self.assertEqual(1, len(nodes), repr(nodes)) 1190 | self.assertEqual(Define('FOO', '42'), nodes[0]) 1191 | 1192 | def test_empty_define(self): 1193 | nodes = list(MakeBuilder('#define FOO').generate()) 1194 | self.assertEqual(1, len(nodes), repr(nodes)) 1195 | self.assertEqual(Define('FOO', ''), nodes[0]) 1196 | 1197 | def test_function_like_define(self): 1198 | nodes = list(MakeBuilder('#define FOO(a, b) a##b').generate()) 1199 | self.assertEqual(1, len(nodes), repr(nodes)) 1200 | self.assertEqual(Define('FOO', 'a##b'), nodes[0]) 1201 | 1202 | def test_function_like_define_no_space(self): 1203 | nodes = list(MakeBuilder('#define FOO(a, b)a##b').generate()) 1204 | self.assertEqual(1, len(nodes), repr(nodes)) 1205 | self.assertEqual(Define('FOO', 'a##b'), nodes[0]) 1206 | 1207 | def test_variable_declaration_with_define(self): 1208 | code = """ 1209 | #define FOO(str) Type##str 1210 | int FOO(name); 1211 | #undef FOO 1212 | void FOO(); 1213 | """ 1214 | nodes = list(MakeBuilder(code).generate()) 1215 | self.assertEqual(3, len(nodes), repr(nodes)) 1216 | self.assertEqual(Define('FOO', 'Type##str'), nodes[0]) 1217 | self.assertEqual(VariableDeclaration('FOO', Type('int')), nodes[1]) 1218 | self.assertEqual(Function('FOO', list(get_tokens('void')), []), 1219 | nodes[2]) 1220 | 1221 | def test_template_function(self): 1222 | nodes = list(MakeBuilder('template <> void equal<0>();').generate()) 1223 | self.assertEqual(1, len(nodes), repr(nodes)) 1224 | self.assertEqual( 1225 | Function('equal', list(get_tokens('void')), [], 1226 | specializations=[Type('0')], 1227 | templated_types={}), 1228 | nodes[0]) 1229 | 1230 | def test_explicit_template_instantiation(self): 1231 | nodes = list( 1232 | MakeBuilder('template class basic_string;').generate()) 1233 | self.assertEqual(1, len(nodes), repr(nodes)) 1234 | self.assertEqual(Class('basic_string'), nodes[0]) 1235 | 1236 | def test_inline_function(self): 1237 | nodes = list(MakeBuilder('inline void fn();').generate()) 1238 | self.assertEqual(1, len(nodes), repr(nodes)) 1239 | self.assertEqual( 1240 | Function('fn', list(get_tokens('void')), []), 1241 | nodes[0]) 1242 | 1243 | 1244 | if __name__ == '__main__': 1245 | unittest.main() 1246 | --------------------------------------------------------------------------------