B
    A!p\Z                 @   s   d Z ddlZddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
 ddl	mZ ddl	mZ ddlmZ dhZd	d
 ZG dd dejZG dd dejZG dd dejZdd Zdd Zdd Zdd ZG dd dejZdd Zdd ZdS )z'Looks for code which can be refactored.    N)reduce)
decorators)
interfaces)checkers)utilszitertools.countc             C   s"   x| j D ]}t||rdS qW dS )NTF)body
isinstance)Zif_nodereturning_node_classnode r   :lib/python3.7/site-packages/pylint/checkers/refactoring.py!_if_statement_is_always_returning+   s    
r   c               @   s  e Zd ZdZejejfZdZddddddd	d
gifdddd	dgifddddddddddddddddZ	dd d!d"d#d$fd%d&d'd(d)ffZ
d*Zdd,d-Zd.d/ Zd0d1 Zejd2d3 Zed4d5 Zd6d7 Zd8d9 Zd:d; Zd<d= Zedd>d? ZeZeZd@dA ZedBddCdD ZedBdEdF ZedBdGdH Z dIdJ Z!dKdL Z"dMdN Z#dOdP Z$edddQdRdSdTdU Z%edVdWdX Z&dYdZ Z'edd[d\d]d^ Z(ed_d`da Z)dbdc Z*eddde Z+dfdg Z,ed_dhdidjdk Z-dldm Z.dndo Z/dpdq Z0edrds Z1dtdu Z2dvdw Z3dxdy Z4edzd{d|d}d~ Z5edd Z6dd Z7eddddd Z8e8Z9dd Z:eddd Z;edd Z<edd Z=dd Z>dd Z?dd Z@dd ZAdd ZBd+S )RefactoringCheckerzLooks for code which can be refactored

    This checker also mixes the astroid and the token approaches
    in order to create knowledge about whether an "else if" node
    is a true "else if" node, or an "elif" node.
    refactoring)z?Consider merging these isinstance calls to isinstance(%s, (%s))zconsider-merging-isinstancezGUsed when multiple consecutive isinstance calls can be merged into one.)zConsider using ternary (%s)zconsider-using-ternaryz=Used when one of known pre-python 2.5 ternary syntax is used.)z*Boolean expression may be simplified to %szsimplify-boolean-expressionz=Emitted when redundant pre-python 2.5 ternary syntax is used.zToo many nested blocks (%s/%s)ztoo-many-nested-blockszvUsed when a function or a method has too many nested blocks. This makes the code less understandable and maintainable.Z	old_names)ZR0101ztoo-many-nested-blocksz(The if statement can be replaced with %szsimplifiable-if-statementz=Used when an if statement can be replaced with 'bool(test)'. )ZR0102zsimplifiable-if-statement)z*Redefining argument with the local name %rzredefined-argument-from-localzUsed when a local name is redefining an argument, which might suggest a potential error. This is taken in account only for a handful of name binding operations, such as for iteration, with statement assignment and exception handler assignment.)zUnnecessary "%s" after "return"zno-else-returnzUsed in order to highlight an unnecessary block of code following an if containing a return statement. As such, it will warn when it encounters an else following a chain of ifs, all of them containing a return statement.)zDisallow trailing comma tupleztrailing-comma-tuplea  In Python, a tuple is actually created by the comma symbol, not by the parentheses. Unfortunately, one can actually create a tuple by misplacing a trailing comma, which can lead to potential weird bugs in your code. You should always use parentheses explicitly for creating a tuple.)zEDo not raise StopIteration in generator, use return statement insteadzstop-iteration-returnzAccording to PEP479, the raise of StopIteration to end the loop of a generator may lead to hard to find bugs. This PEP specify that raise StopIteration has to be replaced by a simple return statement)z_Either all return statements in a function should return an expression, or none of them should.zinconsistent-return-statementszAccording to PEP8, if any return statement returns an expression, any return statements where no value is returned should explicitly state this as return None, and an explicit return statement should be present at the end of the function (if reachable))z+Useless return at end of function or methodzuseless-returnzEmitted when a single "return" or "return None" statement is found at the end of function or method definition. This statement can safely be removed because Python will implicitly return None)z5Consider using tuple unpacking for swapping variableszconsider-swap-variableszYou do not have to use a temporary variable in order to swap variables. Using "tuple unpacking" to directly swap variables makes the intention more clear.)zLConsider using str.join(sequence) for concatenating strings from an iterablezconsider-using-joinznUsing str.join(sequence) is faster, uses less memory and increases readability compared to for-loop iteration.)z2Consider merging these comparisons with "in" to %rzconsider-using-inzTo check if a variable is equal to one of many values,combine the values into a tuple and check if the variable is contained "in" it instead of checking for equality against each of the values.This is faster and less verbose.)z^Consider using dict.get for getting values from a dict if a key is present or a default if notzconsider-using-getzUsing the builtin dict.get for getting a value from a dictionary if a key is present or a default if not, is simpler and considered more idiomatic, although sometimes a bit slower)z0Simplify chained comparison between the operandszchained-comparisonzThis message is emitted when pylint encounters boolean operation like"a < b and b < c", suggesting instead to refactor it to "a < b < c")z)Consider using a dictionary comprehensionz!consider-using-dict-comprehensionzAlthough there is nothing syntactically wrong with this code, it is hard to read and can be simplified to a dict comprehension.Also it is faster since you don't need to create another transient list)z"Consider using a set comprehensionz consider-using-set-comprehensionzAlthough there is nothing syntactically wrong with this code, it is hard to read and can be simplified to a set comprehension.Also it is faster since you don't need to create another transient list)z)The if expression can be replaced with %szsimplifiable-if-expressionz>Used when an if expression can be replaced with 'bool(test)'. )zUnnecessary "%s" after "raise"zno-else-raisezUsed in order to highlight an unnecessary block of code following an if containing a raise statement. As such, it will warn when it encounters an else following a chain of ifs, all of them containing a raise statement.)ZR1701ZR1706ZR1709ZR1702ZR1703ZR1704ZR1705ZR1707ZR1708ZR1710ZR1711ZR1712ZR1713ZR1714ZR1715ZR1716ZR1717ZR1718ZR1719ZR1720zmax-nested-blocks   intz<int>z:Maximum number of nested blocks for function / method body)defaulttypemetavarhelpznever-returning-functions)zsys.exitZcsvzComplete name of functions that never returns. When checking for inconsistent-return-statements if a never returning function is called then it will be considered as an explicit return statement and no message will be printed.)r   r   r   r   Nc             C   s&   t j| | i | _|   d | _d S )N)r   BaseTokenChecker__init___return_nodes_init_never_returning_functions)selflinterr   r   r   r      s    zRefactoringChecker.__init__c             C   s   g | _ g | _d | _t | _d S )N)_nested_blocks_elifsZ_nested_blocks_msgset_reported_swap_nodes)r   r   r   r   r      s    zRefactoringChecker._initc             C   s   t | jj| _d S )N)r   configZnever_returning_functionsr   )r   r   r   r   open   s    zRefactoringChecker.openc             C   s   t j| dd dS )Nzdummy-variables-rgx)r   )
lint_utilsZget_global_option)r   r   r   r   
_dummy_rgx   s    zRefactoringChecker._dummy_rgxc             C   s   t | jtjot | jjtS )N)r   valueastroidConstbool)r
   r   r   r   _is_bool_const   s    z!RefactoringChecker._is_bool_constc             C   s>   t |jtjr:|jj}|r:||gkr:|j|jf| jkr:dS dS )a	  Check if the given node is an actual elif

        This is a problem we're having with the builtin ast module,
        which splits `elif` branches into a separate if statement.
        Unfortunately we need to know the exact type in certain
        cases.
        TF)r   parentr&   Iforelselineno
col_offsetr   )r   r
   r,   r   r   r   _is_actual_elif  s    z"RefactoringChecker._is_actual_elifc       	      C   s"  |  |rdS t|jdks*t|jdkr.dS |jd }|jd }t|tjrxt|tjs^dS | |}| |}d}nzt|tjrt|tjsdS dd |j	D }dd |j	D }|r|sdS t
|t
|krdS | |}| |}d}ndS |r|sdS |jjsdS | jd	||fd
 dS )au  Check if the given if node can be simplified.

        The if statement can be reduced to a boolean expression
        in some cases. For instance, if there are two branches
        and both of them return a boolean value that depends on
        the result of the statement's test, then this can be reduced
        to `bool(test)` without losing any functionality.
        N   r   z'return bool(test)'c             S   s   g | ]}t |tjr|jqS r   )r   r&   
AssignNamename).0targetr   r   r   
<listcomp>2  s   z=RefactoringChecker._check_simplifiable_if.<locals>.<listcomp>c             S   s   g | ]}t |tjr|jqS r   )r   r&   r1   r2   )r3   r4   r   r   r   r5   7  s   z'var = bool(test)'zsimplifiable-if-statement)r
   args)r/   lenr,   r   r   r&   Returnr)   Assigntargetssortedr%   add_message)	r   r
   Zfirst_branchZelse_branchZfirst_branch_is_boolZelse_branch_is_bool
reduced_toZfirst_branch_targetsZelse_branch_targetsr   r   r   _check_simplifiable_if  s@    










z)RefactoringChecker._check_simplifiable_ifc             C   sz   xtt |D ]h\}}|d }|dkrH| j|| d ||d  d g q
t||r
| jdr
| jd|jd d q
W d S )Nr0   elif   ztrailing-comma-tupler   )line)	enumerater   extendis_trailing_commar   Zis_message_enabledr<   start)r   tokensindextokenZtoken_stringr   r   r   process_tokensZ  s    &
z!RefactoringChecker.process_tokensc             C   s   |    d S )N)r   )r   _r   r   r   leave_modulej  s    zRefactoringChecker.leave_modulec             C   s   |  | d S )N)_check_nested_blocks)r   r
   r   r   r   visit_tryexceptm  s    z"RefactoringChecker.visit_tryexceptc             C   s~   | j r| j |jrd S |js"d S | }t|tjs:d S x>|jj	tj
tjfdD ]$}|j|jkrR| jd||jfd qRW d S )N)
skip_klasszredefined-argument-from-local)r
   r6   )r$   matchr2   r-   scoper   r&   FunctionDefr6   nodes_of_classr1   ZLambdar<   )r   Z	name_noderP   Zdefined_argumentr   r   r   $_check_redefined_argument_from_localt  s    z7RefactoringChecker._check_redefined_argument_from_localzredefined-argument-from-localc             C   s0   |  | x |jtjD ]}| | qW d S )N)rL   r4   rR   r&   r1   rS   )r   r
   r2   r   r   r   	visit_for  s    
zRefactoringChecker.visit_forc             C   s$   |j r t|j tjr | |j  d S )N)r2   r   r&   r1   rS   )r   r
   r   r   r   visit_excepthandler  s    z&RefactoringChecker.visit_excepthandlerc             C   s>   x8|j D ].\}}|sqx|tjD ]}| | q$W qW d S )N)itemsrR   r&   r1   rS   )r   r
   rJ   namesr2   r   r   r   
visit_with  s
    zRefactoringChecker.visit_withc             C   sZ   |j s
d S | |rd S t||rV|j d }|j|jf| jk}| j|||rNdndd d S )Nr   r?   else)r
   r6   )r,   r/   r   r-   r.   r   r<   )r   r
   msg_idr	   r,   Zfollowed_by_elifr   r   r   _check_superfluous_else  s    


z*RefactoringChecker._check_superfluous_elsec             C   s   | j |dtjdS )Nzno-else-return)rZ   r	   )r[   r&   r8   )r   r
   r   r   r   _check_superfluous_else_return  s    z1RefactoringChecker._check_superfluous_else_returnc             C   s   | j |dtjdS )Nzno-else-raise)rZ   r	   )r[   r&   Raise)r   r
   r   r   r   _check_superfluous_else_raise  s    z0RefactoringChecker._check_superfluous_else_raisec             C   st  dd }t |jtjot|jdkot |jd tjot |jd jtjo||jd jj|jj	d d ot |jd jj
tjo||jd jj
j|jjot|jd jdkot |jd jd tjot t|jj	d d tj}|r|js| jd|d nr|rpt|jdkrpt |jd tjrp||jd jd |jd jd rpt|jd jdkrp| jd|d d S )Nc                sb   x:t jt jgD ]* t fdd| |gD r| j|jkS qW tdd | |gD r^| j|jkS dS )Nc             3   s   | ]}t | V  qd S )N)r   )r3   _node)_typer   r   	<genexpr>  s    zZRefactoringChecker._check_consider_get.<locals>.type_and_name_are_equal.<locals>.<genexpr>c             s   s   | ]}t |tjV  qd S )N)r   r&   r'   )r3   r_   r   r   r   ra     s    F)r&   Namer1   allr2   r%   )Znode_aZnode_br   )r`   r   type_and_name_are_equal  s    zGRefactoringChecker._check_consider_get.<locals>.type_and_name_are_equalr0   r   zconsider-using-get)r
   )r   testr&   Comparer7   r   r9   r%   	SubscriptopssliceIndexleftr:   r1   r   
safe_inferDictr,   r<   )r   r
   rd   Zif_block_okr   r   r   _check_consider_get  s(    	 
"z&RefactoringChecker._check_consider_getzno-else-returnzno-else-raisezconsider-using-getc             C   s6   |  | | | | | | | | | d S )N)r>   rL   r\   r^   rn   )r   r
   r   r   r   visit_if  s
    



zRefactoringChecker.visit_ifzsimplifiable-if-expressionc             C   s   |  | d S )N)_check_simplifiable_ifexp)r   r
   r   r   r   visit_ifexp  s    zRefactoringChecker.visit_ifexpc             C   s   t |jtjrt |jtjs d S t |jjtr<t |jjts@d S t |jtjrTd}nd}|jj|jjfdkrxd	|}n|jj|jjfdkrd}nd S | j
d||fd d S )	Nre   z
bool(test))TFz'{}')FTz
'not test'zsimplifiable-if-expression)r
   r6   )r   r   r&   r'   r,   r%   r(   re   rf   formatr<   )r   r
   Ztest_reduced_tor=   r   r   r   rp     s    z,RefactoringChecker._check_simplifiable_ifexpzinconsistent-return-statementszuseless-returnc             C   s6   |  | j g | _| | | | g | j|j< d S )N)%_emit_nested_blocks_message_if_neededr   _check_consistent_returns_check_return_at_the_endr   r2   )r   r
   r   r   r   leave_functiondef  s
    

z$RefactoringChecker.leave_functiondefzstop-iteration-returnc             C   s   |  | d S )N)&_check_stop_iteration_inside_generator)r   r
   r   r   r   visit_raise  s    zRefactoringChecker.visit_raisec             C   sx   |  }t|tjr| s dS t|tr0dS |js:dS t	|j}|dksX|tj
kr\dS | |rt| jd|d dS )zHCheck if an exception of type StopIteration is raised inside a generatorNzstop-iteration-return)r
   )framer   r&   rQ   is_generatorr   node_ignores_exceptionStopIterationexcrl   Uninferable+_check_exception_inherit_from_stopiterationr<   )r   r
   ry   r}   r   r   r   rw     s    
z9RefactoringChecker._check_stop_iteration_inside_generatorc                s&   d tj t fdd|  D S )zHReturn True if the exception node in argument inherit from StopIterationz{}.StopIterationc             3   s   | ]}|   kV  qd S )N)qname)r3   Z_class)stopiteration_qnamer   r   ra   *  s    zQRefactoringChecker._check_exception_inherit_from_stopiteration.<locals>.<genexpr>)rr   r   ZEXCEPTIONS_MODULEanymro)r}   r   )r   r   r   &  s    z>RefactoringChecker._check_exception_inherit_from_stopiterationc             C   sR   t |jtjrN|jrN|jjdkrNt |jd tjrNd|jj}| j||d d S )N>   dictr   r   zconsider-using-{}-comprehension)r
   )	r   funcr&   rb   r6   r2   ZListComprr   r<   )r   r
   Zmessage_namer   r   r   /_check_consider_using_comprehension_constructor,  s    zBRefactoringChecker._check_consider_using_comprehension_constructorz!consider-using-dict-comprehensionz consider-using-set-comprehensionc             C   s   |  | | | d S )N)3_check_raising_stopiteration_in_generator_next_callr   )r   r
   r   r   r   
visit_call6  s    
zRefactoringChecker.visit_callc             C   s   dd }t |jtjrdS t|j}t|dddkr| }t|j	dk}t |tj
r| r|st|ts||j	d s| jd	|d
 dS )a  Check if a StopIteration exception is raised by the call to next function

        If the next value has a default value, then do not add message.

        :param node: Check to see if this Call node is a next function
        :type node: :class:`astroid.node_classes.Call`
        c             S   s   t | }|r| tkS dS )NF)r   rl   r   KNOWN_INFINITE_ITERATORS)Zparaminferredr   r   r   _looks_like_infinite_iteratorH  s    
zmRefactoringChecker._check_raising_stopiteration_in_generator_next_call.<locals>._looks_like_infinite_iteratorNr2    nextr0   r   zstop-iteration-return)r
   )r   r   r&   Z	Attributer   rl   getattrry   r7   r6   rQ   rz   r{   r|   r<   )r   r
   r   r   ry   Zhas_sentinel_valuer   r   r   r   ?  s    	zFRefactoringChecker._check_raising_stopiteration_in_generator_next_callc             C   s   t | tjsdS | jdd }|j| kr:|g| _n\x(t| jD ]}||jkrVP | j  qFW t |tjr| 	|r| jr| j  | j
| t|t| jkr| | dS )z5Update and check the number of nested blocks
        N)r   rP   r&   rQ   r   r*   reversedpopr+   r/   appendr7   rs   )r   r
   nested_blocksZancestor_noder   r   r   rL   a  s    


z'RefactoringChecker._check_nested_blocksc             C   s4   t || jjkr0| jd|d t || jjfd d S )Nztoo-many-nested-blocksr   )r
   r6   )r7   r!   Zmax_nested_blocksr<   )r   r   r   r   r   rs   |  s
    z8RefactoringChecker._emit_nested_blocks_message_if_neededc                s   t   tt }x| jD ]}t|tjrt|jdkr8qt	
|j}|rt	|sTq|jdkr`q|jd  }|jd }||kr | t|tjrdd | D }n
| g}|| | qW  fdd| D S )	aH  Get the duplicated types from the underlying isinstance calls.

        :param astroid.BoolOp node: Node which should contain a bunch of isinstance calls.
        :returns: Dictionary of the comparison objects from the isinstance calls,
                  to duplicate values from consecutive calls.
        :rtype: dict
        r@   r   r   r0   c             S   s   g | ]}|  qS r   )	as_string)r3   Z
class_typer   r   r   r5     s    zCRefactoringChecker._duplicated_isinstance_types.<locals>.<listcomp>c                s   i | ]\}}| kr||qS r   r   )r3   keyr%   )duplicated_objectsr   r   
<dictcomp>  s   zCRefactoringChecker._duplicated_isinstance_types.<locals>.<dictcomp>)r   collectionsdefaultdictvaluesr   r&   Callr7   r6   r   rl   r   is_builtin_objectr2   r   addZTupleZiteredupdaterV   )r
   Z	all_typesZcallr   Zisinstance_objectZisinstance_typesZelemsr   )r   r   _duplicated_isinstance_types  s(    	





z/RefactoringChecker._duplicated_isinstance_typesc             C   s^   |j dkrdS | |}x@| D ]4\}}tdd |D }| jd||d|fd q"W dS )z4Check isinstance calls which can be merged together.orNc             s   s   | ]
}|V  qd S )Nr   )r3   r2   r   r   r   ra     s    zHRefactoringChecker._check_consider_merging_isinstance.<locals>.<genexpr>zconsider-merging-isinstancez, )r
   r6   )opr   rV   r;   r<   join)r   r
   Z
first_argsZduplicated_nameZclass_namesrW   r   r   r   "_check_consider_merging_isinstance  s    

z5RefactoringChecker._check_consider_merging_isinstancec             C   s  ddd}|j |ks"t|jdk r&d S xr|jD ]h}t|tjrdt|jdksd|jd d ||j  krhd S x,|j|jd d fD ]}t|tjr~d S q~W q.W g g  }}xd|jD ]Z}t	 }xD|j|jd d fD ],}t|tj
r||  ||  qW || qW tdd |}|s"d S tt|d }	|j d	krBd
nd}
ttj|}||	 t|dkrxd|n
|d d }d|	|
|f }| jd||fd d S )Nz==z!=)r   andr@   r0   r   c             S   s
   |  |S )N)intersection)abr   r   r   <lambda>  s    z=RefactoringChecker._check_consider_using_in.<locals>.<lambda>r   inznot inz, ,z
%s %s (%s)zconsider-using-in)r
   r6   )r   r7   r   r   r&   rf   rh   rk   r   r   rb   r   r   r   r   r;   listr   OrderedDictfromkeysremover   r<   )r   r
   Zallowed_opsr%   Z
comparableZ	variablesr   Zvariable_setZcommon_variablesZcommon_variableZcomprehensionZvalues_string
suggestionr   r   r   _check_consider_using_in  s:    


$z+RefactoringChecker._check_consider_using_inc       
      C   s   |j dkst|jdk rdS dd }tdd }x$|jD ]}t|tjr:||| q:W xb| D ]V\}}t|d 	|d	 }t|d }t|d	 }	||k rb||	k rb| j
d
|d P qbW dS )a	  Check if there is any chained comparison in the expression.

        Add a refactoring message if a boolOp contains comparison like a < b and b < c,
        which can be chained as a < b < c.

        Care is taken to avoid simplifying a < b < c and b < d.
        r   r@   Nc             S   s   | j }x| jD ]\}}x||fD ]}d }t|tjr<|j}nt|tjrN|j}|d krXq |dkr||kr||| d |  q||kr|| d |  q |dkr ||kr|| d |  q ||kr || d |  q W |}qW d S )N)<z<=lower_boundupper_bound)>z>=)	rk   rh   r   r&   rb   r2   r'   r%   r   )comparison_nodeusesZleft_operandoperatorZright_operandoperandr%   r   r   r   _find_lower_upper_bounds  s*    zNRefactoringChecker._check_chained_comparison.<locals>._find_lower_upper_boundsc               S   s   t  t  dS )N)r   r   )r   r   r   r   r   r     s    z>RefactoringChecker._check_chained_comparison.<locals>.<lambda>r   r   zchained-comparison)r
   )r   r7   r   r   r   r   r&   rf   rV   r   r<   )
r   r
   r   r   r   rJ   ZboundsZ
num_sharedZnum_lower_boundsZnum_upper_boundsr   r   r   _check_chained_comparison  s    
z,RefactoringChecker._check_chained_comparisonzconsider-merging-isinstancezconsider-using-inzchained-comparisonc             C   s"   |  | | | | | d S )N)r   r   r   )r   r
   r   r   r   visit_boolop  s    

zRefactoringChecker.visit_boolopc             C   s>   t | tjo<t| jdko<t | jd tjjo<t | jtjjS )Nr0   r   )	r   r&   r9   r7   r:   Znode_classesr1   r%   rb   )r
   r   r   r   _is_simple_assignment"  s    z(RefactoringChecker._is_simple_assignmentc                s   |  r|    sd S ||  |    g}t fdd|D sHd S t fdd|D rbd S dd |D }dd |D }|d |d kr|d	d  |d d krĈ j| d
} j||d d S )Nc             3   s   | ]}  |V  qd S )N)r   )r3   r
   )r   r   r   ra   /  s    z;RefactoringChecker._check_swap_variables.<locals>.<genexpr>c             3   s   | ]}| j kV  qd S )N)r    )r3   r
   )r   r   r   ra   1  s    c             S   s   g | ]}|j d  jqS )r   )r:   r2   )r3   r
   r   r   r   r5   3  s    z<RefactoringChecker._check_swap_variables.<locals>.<listcomp>c             S   s   g | ]}|j jqS r   )r%   r2   )r3   r
   r   r   r   r5   4  s    r   r0   zconsider-swap-variables)r
   )Znext_siblingrc   r   r    r   r<   )r   r
   Zassignmentsrk   rightmessager   )r   r   _check_swap_variables+  s    (z(RefactoringChecker._check_swap_variableszsimplify-boolean-expressionzconsider-using-ternaryzconsider-swap-variablesc       	      C   s   |  | | |jr*| |j\}}}nd S tdd ||fD rHd S t|}|d tjfkrfd}n|	 }|dkrd}|
 }n d}dj|
 |
 |
 d}| j|||fd	 d S )
Nc             s   s   | ]}t |tjV  qd S )N)r   r&   rf   )r3   r%   r   r   r   ra   G  s    z2RefactoringChecker.visit_assign.<locals>.<genexpr>TFzsimplify-boolean-expressionzconsider-using-ternaryz{truth} if {cond} else {false})truthcondZfalse)r
   r6   )r   _is_and_or_ternaryr%   _and_or_ternary_argumentsrc   r   rl   r&   r~   Z
bool_valuer   rr   r<   )	r   r
   r   Ztruth_valuefalse_valueZinferred_truth_valueZtruth_boolean_valuer   r   r   r   r   visit_assign:  s(    


zRefactoringChecker.visit_assignc             C   s   |j }t|tjr t|jdkr$dS | }t|tjs<dS dd |jD }|j	dkot|j
tjot|jdko|j
j|kot|jtjot|jjtot|jtjo|jj|j
jk}|r| jd|d dS )a  
        We start with the augmented assignment and work our way upwards.
        Names of variables for nodes if match successful:
        result = ''  # assign
        for number in ['1', '2', '3']  # for_loop
            result += number  # aug_assign
        r0   Nc             S   s   h | ]}t |tjr|jqS r   )r   r&   r1   r2   )r3   r4   r   r   r   	<setcomp>m  s   z@RefactoringChecker._check_consider_using_join.<locals>.<setcomp>z+=zconsider-using-join)r
   )r*   r   r&   Forr7   r   Zprevious_siblingr9   r:   r   r4   r1   r2   r%   r'   strrb   r<   )r   Z
aug_assignZfor_loopZassignZresult_assign_namesZis_concat_loopr   r   r   _check_consider_using_join_  s$    

z-RefactoringChecker._check_consider_using_joinzconsider-using-joinc             C   s   |  | d S )N)r   )r   r
   r   r   r   visit_augassign  s    z"RefactoringChecker.visit_augassignc             C   s   t | tjo| jdkot| jdkot | jd tjot | jd tj o| jd jdkot | jd jd tj ot| jd jdkS )z
        Returns true if node is 'condition and true_value or false_value' form.

        All of: condition, true_value and false_value should not be a complex boolean expression
        r   r@   r   r0   r   )r   r&   BoolOpr   r7   r   )r
   r   r   r   r     s    
z%RefactoringChecker._is_and_or_ternaryc             C   s$   | j d }| j d j \}}|||fS )Nr0   r   )r   )r
   r   Z	conditionZ
true_valuer   r   r   r     s    
z,RefactoringChecker._and_or_ternary_argumentsc             C   s"   t |jtjtjd| j|j< d S )N)rN   )r   rR   r&   r8   rQ   r   r2   )r   r
   r   r   r   visit_functiondef  s    z$RefactoringChecker.visit_functiondefc             C   sV   dd | j |j D }|sdS t|t| j |j krD| |rDdS | jd|d dS )a{  Check that all return statements inside a function are consistent.

        Return statements are consistent if:
            - all returns are explicit and if there is no implicit return;
            - all returns are empty and if there is, possibly, an implicit return.

        Args:
            node (astroid.FunctionDef): the function holding the return statements.

        c             S   s   g | ]}|j d k	r|qS )N)r%   )r3   r_   r   r   r   r5     s    z@RefactoringChecker._check_consistent_returns.<locals>.<listcomp>Nzinconsistent-return-statements)r
   )r   r2   r7   _is_node_return_endedr<   )r   r
   Zexplicit_returnsr   r   r   rt     s    
z,RefactoringChecker._check_consistent_returnsc                s\  t |tjrdS t |tjrTy |j d } |r:dS W n tjk
rR   Y nX t |tjrddS t |tj	r|j
szdS t|sdS t|j
}|dks|tjkrdS | dd }t||}|dk	rt|ng }|rt fdd|D S dS t |tjrBt fd	d|jD }t fd
d|jD }|o@|S t fdd| D S )zCheck if the node ends with an explicit return statement.

        Args:
            node (astroid.NodeNG): node to be checked.

        Returns:
            bool: True if the node ends with an explicit statement, False otherwise.

        Tr   NF.r   c             3   s   | ]}  |V  qd S )N)r   )r3   Z_handler)r   r   r   ra     s    z;RefactoringChecker._is_node_return_ended.<locals>.<genexpr>c             3   s$   | ]}t |tjs |V  qd S )N)r   r&   rQ   r   )r3   Z_ore)r   r   r   ra     s   c             3   s$   | ]}t |tjs |V  qd S )N)r   r&   rQ   r   )r3   Z_ifn)r   r   r   ra     s   c             3   s$   | ]}t |tjs |V  qd S )N)r   r&   ZExceptHandlerr   )r3   Z_child)r   r   r   ra     s   )r   r&   r8   r   r   r    _is_function_def_never_returningZInferenceErrorWhiler]   r}   r   Zis_node_inside_try_exceptrl   r~   ZpytypesplitZget_exception_handlersr   r   r+   r,   r   Zget_children)r   r
   Zfuncdef_noder}   Zexc_nameZhandlersZis_orelse_returningZis_if_returningr   )r   r   r     sJ    





z(RefactoringChecker._is_node_return_endedc             C   s(   y|  | jkS  tk
r"   dS X dS )zReturn True if the function never returns. False otherwise.

        Args:
            node (astroid.FunctionDef): function definition node to be analyzed.

        Returns:
            bool: True if the function never returns, False otherwise.
        FN)r   r   	TypeError)r   r
   r   r   r   r     s    	z3RefactoringChecker._is_function_def_never_returningc             C   s   t | j|j dkrdS t |jdkr*dS |jd }t|tjr|jdkrZ| jd|d n(t|jtj	r|jjdkr| jd|d dS )a  Check for presence of a *single* return statement at the end of a
        function. "return" or "return None" are useless because None is the
        default return type if they are missing.

        NOTE: produces a message only if there is a single return statement
        in the function body. Otherwise _check_consistent_returns() is called!
        Per its implementation and PEP8 we can have a "return None" at the end
        of the function body if there are other return statements before that!
        r0   Nr   zuseless-return)r
   )
r7   r   r2   r   r   r&   r8   r%   r<   r'   )r   r
   Zlastr   r   r   ru     s    


z+RefactoringChecker._check_return_at_the_end)N)C__name__
__module____qualname____doc__r   ZITokenCheckerIAstroidChecker__implements__r2   msgsoptionspriorityr   r   r"   r   Zcachedpropertyr$   staticmethodr)   r/   r>   rI   rK   r   check_messagesrM   Zvisit_tryfinallyZvisit_whilerS   rT   rU   rX   r[   r\   r^   rn   ro   rq   rp   rv   rx   rw   r   r   r   r   rL   rs   r   r   r   r   r   r   r   r   Zvisit_returnr   r   r   r   r   rt   r   r   ru   r   r   r   r   r   2   s   

			
G#	
"*+3	 !Ir   c               @   sR   e Zd ZejfZdZdddZedd Z	e
ddd	 Ze
d
dd ZdS )RecommandationCheckerr   )z@Consider using enumerate instead of iterating with range and lenzconsider-using-enumeratez~Emitted when code that iterates with range and len is encountered. Such code can be simplified by using the enumerate builtin.)zEConsider iterating the dictionary directly instead of calling .keys()zconsider-iterating-dictionaryzEmitted when the keys of a dictionary are iterated through the .keys() method. It is enough to just iterate through the dictionary itself, as in "for key in dictionary".)ZC0200ZC0201c             C   s&   t | }|sdS t |o$|j|kS )NF)r   rl   r   r2   )r
   Zfunctionr   r   r   r   _is_builtin<  s    
z!RecommandationChecker._is_builtinzconsider-iterating-dictionaryc             C   sf   t |j}|sd S t|tjs$d S t|jtjr<|jdkr@d S t|j	tj
tjfrb| jd|d d S )Nkeyszconsider-iterating-dictionary)r
   )r   rl   r   r   r&   ZBoundMethodZboundrm   r2   r*   r   ZComprehensionr<   )r   r
   r   r   r   r   r   C  s    z RecommandationChecker.visit_callzconsider-using-enumeratec             C   s  t |jtjsdS | |jjds&dS t|jjdkrJt|jjd sJdS t|jjdkr^dS t |jjd tjsvdS |jjd j}| |dsdS |jjd j}|rt|dkrdS |d }t |tj	sdS |
 }|jdkr|jd	krdS x|jD ]}x|tjD ]}t |jtj	s qt |jtjs4qt |jjtj	sJq|jjj|jjkrbq|j|jjkrvq|j
 |
 krq| jd
|d dS W qW dS )z?Emit a convention whenever range and len are used for indexing.Nranger@   r   r   r7   r0   r   __iter__zconsider-using-enumerate)r
   )r   iterr&   r   r   r   r7   r6   _is_constant_zerorb   rP   r2   r   rR   rg   r%   ri   rj   r4   r<   )r   r
   Zsecond_funcZlen_argsZiterating_objectrP   ZchildZ	subscriptr   r   r   rT   P  sL     zRecommandationChecker.visit_forN)r   r   r   r   r   r   r2   r   r   r   r   r   r   rT   r   r   r   r   r   (  s   r   c            	   @   sb   e Zd ZdZejfZddiZdZddddd	d
dddZ	e
jfZdd dD Zeddd ZdS )
NotCheckerzchecks for too many not in comparison expressions

    - "not not" should trigger a warning
    - "not" followed by a comparison should trigger a warning
    ZC0113)zConsider changing "%s" to "%s"zunneeded-notz=Used when a boolean expression contains an unneeded negation.r   z>=r   z<=r   z!=z==znot inzis not)r   z<=r   z>=z==z!=r   isc             C   s   g | ]}d t j|f qS )z%s.%s)builtinsr   )r3   r   r   r   r   r5     s    zNotChecker.<listcomp>)r   	frozensetzunneeded-notc       	      C   s6  |j dkrd S |j}t|tjrJ|j dkrJ| jd|| |j fd nt|tjr2|j}t	|j
dkrpd S |j
d \}}|| jkrd S | }|jdkr|dkrd S xRt|t|fD ]:}|sd S t|| jrd S t|tjr| | jkrd S qW d| | j| | f }| jd|| |fd d S )	Nnotzunneeded-not)r
   r6   r0   r   __ne__z==z%s %s %s)r   r   r   r&   UnaryOpr<   r   rf   rk   r7   rh   
reverse_opry   r2   r   Z	node_typeskipped_nodesZInstancer   skipped_classnames)	r   r
   r   rk   r   r   ry   r`   r   r   r   r   visit_unaryop  s@    

zNotChecker.visit_unaryopN)r   r   r   r   r   r   r   r   r2   r   r&   Setr   r   r   r   r   r   r   r   r   r     s    r   c             C   s&   t | tjo$t | jtjo$| jjdkS )z!Checks if node is len(SOMETHING).r7   )r   r&   r   r   rb   r2   )r
   r   r   r   _is_len_call  s    r   c             C   s   t | tjo| jdkS )Nr   )r   r&   r'   r%   )r
   r   r   r   r     s    r   c             C   s   t | tjo| j|kS )N)r   r&   r'   r%   )r
   r%   r   r   r   _has_constant_value  s    r   c             C   s   t | tjtjtjtjfS )zC Checks if node is an if, while, assert or if expression statement.)r   r&   r+   r   ZAssertZIfExp)r
   r   r   r   _node_is_test_condition  s    r   c               @   sb   e Zd ZdZejfZdZddiZdZ	dZ
eddd	 Zedd
d Zeddd ZdS )
LenCheckera  Checks for incorrect usage of len() inside conditions.
    Pep8 states:
    For sequences, (strings, lists, tuples), use the fact that empty sequences are false.

        Yes: if not seq:
             if seq:

        No: if len(seq):
            if not len(seq):

    Problems detected:
    * if len(sequence):
    * if not len(sequence):
    * if len(sequence) == 0:
    * if len(sequence) != 0:
    * if len(sequence) > 0:
    * if len(sequence) < 1:
    * if len(sequence) <= 0:
    r   ZC1801)z>Do not use `len(SEQUENCE)` to determine if a sequence is emptyzlen-as-conditionzUsed when Pylint detects that len(sequence) is being used inside a condition to determine if a sequence is empty. Instead of comparing the length to 0, rely on the fact that empty sequences are false.r   zlen-as-conditionc             C   s^   t |rZ|j}xt|tjr$|j}qW t|s2d S ||jksL|j|sLd S | jd|d d S )Nzlen-as-condition)r
   )	r   r*   r   r&   r   r   re   Z	parent_ofr<   )r   r
   r*   r   r   r   r     s    
zLenChecker.visit_callc             C   s2   t |tjr.|jdkr.t|jr.| jd|d dS )z`not len(S)` must become `not S` regardless if the parent block
        is a test condition or something else (boolean expression)
        e.g. `if not len(S):`r   zlen-as-condition)r
   N)r   r&   r   r   r   r   r<   )r   r
   r   r   r   r   +  s    

zLenChecker.visit_unaryopc       	      C   s"  d|j fg}||j ttj| }xtt|d D ]}|| }||d  }||d  }d}t|r~|dkr~t	|r~d}n`t	|r|dkrt|rd}nBt
|ddr|d	krt	|rd}n t	|r|d
krt
|ddrd}|r8|j}x|rt|s|j}qW t|r8| jd|d q8W d S )Nr   r@   r0   F)z==z!=r   z>=T)z==z!=r   z<=)r%   r   r   zlen-as-condition)r
   )rk   rC   rh   r   	itertoolschainr   r7   r   r   r   r*   r   r<   )	r   r
   rh   Zops_idxZop_1Zop_2Zop_3Zerror_detectedr*   r   r   r   visit_compare7  s<    	
zLenChecker.visit_compareN)r   r   r   r   r   r   r   r2   r   r   r   r   r   r   r   r   r   r   r   r   r     s   	r   c       
         s     }|j tjkrdS t d d}tt|fdd|}tdd |D }|r\|s`dS  fdd	}| }d
dh}x,|  D ]}	d|	jks|	j|krdS qW dS )a  Check if the given token is a trailing comma

    :param tokens: Sequence of modules tokens
    :type tokens: list[tokenize.TokenInfo]
    :param int index: Index of token under check in tokens
    :returns: True if the token is a comma which trails an expression
    :rtype: bool
    Fr0   Nc             S   s   | j d |j d kS )Nr   )rE   )other_tokenZ_tokenr   r   r   r     s    z#is_trailing_comma.<locals>.<lambda>c             s   s    | ]}|j tjtjfkV  qd S )N)r   tokenizeNEWLINECOMMENT)r3   r   r   r   r   ra     s   z$is_trailing_comma.<locals>.<genexpr>c                 s@   x:t td  D ]"\} }|jtjtjfkr |  S qW dS )z4Get the index denoting the start of the current lineNr   )rB   r   r   r   r   NL)ZsubindexrH   )rG   rF   r   r   get_curline_index_start  s    z2is_trailing_comma.<locals>.get_curline_index_startreturnyield=T)	
exact_typer   COMMAr   islicer   	takewhilerc   string)
rF   rG   rH   Zleft_tokensZsame_line_remaining_tokensZis_last_elementr   Zcurline_startZexpected_tokensZ	prevtokenr   )rG   rF   r   rD   r  s(    	

rD   c             C   s<   |  t|  |  t|  |  t|  |  t|  dS )z.Required method to auto register this checker.N)Zregister_checkerr   r   r   r   )r   r   r   r   register  s    r  )r   r   	functoolsr   r   r   r   r&   r   Zpylintr   r   r   r#   Zpylint.checkersr   r   r   r   ZBaseCheckerr   r   r   r   r   r   r   rD   r  r   r   r   r   <module>   s>          }dN	 -