@@ -152,12 +152,18 @@ def find_copy_constructor(type_):
152152 return None
153153
154154
155- def find_noncopyable_vars (type_ ):
155+ def find_noncopyable_vars (type_ , already_visited_cls_vars = None ):
156156 """
157157 Returns list of all `noncopyable` variables.
158158
159+ If an already_visited_cls_vars list is provided as argument, the returned
160+ list will not contain these variables. This list will be extended with
161+ whatever variables pointing to classes have been found.
162+
159163 Args:
160164 type_ (declarations.class_t): the class to be searched.
165+ already_visited_cls_vars (list): optional list of vars that should not
166+ be checked a second time, to prevent infinite recursions.
161167
162168 Returns:
163169 list: list of all `noncopyable` variables.
@@ -172,6 +178,9 @@ def find_noncopyable_vars(type_):
172178 allow_empty = True )
173179 noncopyable_vars = []
174180
181+ if already_visited_cls_vars is None :
182+ already_visited_cls_vars = []
183+
175184 message = (
176185 "__contains_noncopyable_mem_var - %s - TRUE - " +
177186 "contains const member variable" )
@@ -196,7 +205,13 @@ def find_noncopyable_vars(type_):
196205 if class_traits .is_my_case (type_ ):
197206
198207 cls = class_traits .get_declaration (type_ )
199- if is_noncopyable (cls ):
208+
209+ # Exclude classes that have already been visited.
210+ if cls in already_visited_cls_vars :
211+ continue
212+ already_visited_cls_vars .append (cls )
213+
214+ if is_noncopyable (cls , already_visited_cls_vars ):
200215 logger .debug ((message + " - class that is not copyable" )
201216 % type_ .decl_string )
202217 noncopyable_vars .append (mvar )
@@ -632,8 +647,20 @@ def is_convertible(source, target):
632647 return __is_convertible_t (source , target ).is_convertible ()
633648
634649
635- def __is_noncopyable_single (class_ ):
636- """implementation details"""
650+ def __is_noncopyable_single (class_ , already_visited_cls_vars = None ):
651+ """
652+ Implementation detail.
653+
654+ Checks if the class is non copyable, without considering the base classes.
655+
656+ Args:
657+ class_ (declarations.class_t): the class to be checked
658+ already_visited_cls_vars (list): optional list of vars that should not
659+ be checked a second time, to prevent infinite recursions.
660+
661+ Returns:
662+ bool: if the class is non copyable
663+ """
637664 # It is not enough to check base classes, we should also to check
638665 # member variables.
639666 logger = utils .loggers .cxx_parser
@@ -650,7 +677,11 @@ def __is_noncopyable_single(class_):
650677 " public destructor: yes" ])
651678 logger .debug (msg )
652679 return False
653- if find_noncopyable_vars (class_ ):
680+
681+ if already_visited_cls_vars is None :
682+ already_visited_cls_vars = []
683+
684+ if find_noncopyable_vars (class_ , already_visited_cls_vars ):
654685 logger .debug (
655686 ("__is_noncopyable_single(TRUE) - %s - contains noncopyable " +
656687 "members" ) % class_ .decl_string )
@@ -662,9 +693,22 @@ def __is_noncopyable_single(class_):
662693 return False
663694
664695
665- def is_noncopyable (class_ ):
666- """returns True, if class is noncopyable, False otherwise"""
696+ def is_noncopyable (class_ , already_visited_cls_vars = None ):
697+ """
698+ Checks if class is non copyable
699+
700+ Args:
701+ class_ (declarations.class_t): the class to be checked
702+ already_visited_cls_vars (list): optional list of vars that should not
703+ be checked a second time, to prevent infinite recursions.
704+ In general you can ignore this argument, it is mainly used during
705+ recursive calls of is_noncopyable() done by pygccxml.
706+
707+ Returns:
708+ bool: if the class is non copyable
709+ """
667710 logger = utils .loggers .cxx_parser
711+
668712 class_decl = class_traits .get_declaration (class_ )
669713
670714 true_header = "is_noncopyable(TRUE) - %s - " % class_ .decl_string
@@ -683,6 +727,9 @@ def is_noncopyable(class_):
683727 if copy_ and copy_ .access_type == 'public' and not copy_ .is_artificial :
684728 return False
685729
730+ if already_visited_cls_vars is None :
731+ already_visited_cls_vars = []
732+
686733 for base_desc in class_decl .recursive_bases :
687734 assert isinstance (base_desc , class_declaration .hierarchy_info_t )
688735
@@ -700,13 +747,15 @@ def is_noncopyable(class_):
700747 true_header +
701748 "there is private copy constructor" )
702749 return True
703- elif __is_noncopyable_single (base_desc .related_class ):
750+ elif __is_noncopyable_single (
751+ base_desc .related_class , already_visited_cls_vars ):
704752 logger .debug (
705753 true_header +
706754 "__is_noncopyable_single returned True" )
707755 return True
708756
709- if __is_noncopyable_single (base_desc .related_class ):
757+ if __is_noncopyable_single (
758+ base_desc .related_class , already_visited_cls_vars ):
710759 logger .debug (
711760 true_header +
712761 "__is_noncopyable_single returned True" )
@@ -722,7 +771,7 @@ def is_noncopyable(class_):
722771 logger .debug (true_header + "has private destructor" )
723772 return True
724773 else :
725- return __is_noncopyable_single (class_decl )
774+ return __is_noncopyable_single (class_decl , already_visited_cls_vars )
726775
727776
728777def is_unary_operator (oper ):
0 commit comments