@@ -127,6 +127,31 @@ class Definition(Binding):
127127 """
128128
129129
130+ class UnhandledKeyType (object ):
131+ """
132+ A dictionary key of a type that we cannot or do not check for duplicates.
133+ """
134+
135+
136+ class VariableKey (object ):
137+ """
138+ A dictionary key which is a variable (ast.Name).
139+
140+ @ivar name: The name of the variable.
141+ """
142+ def __init__ (self , name ):
143+ self .name = name
144+
145+ def __eq__ (self , compare ):
146+ return (
147+ compare .__class__ == self .__class__
148+ and compare .name == self .name
149+ )
150+
151+ def __hash__ (self ):
152+ return hash (self .name )
153+
154+
130155class Importation (Definition ):
131156 """
132157 A binding created by an import statement.
@@ -849,7 +874,7 @@ def ignore(self, node):
849874 PASS = ignore
850875
851876 # "expr" type nodes
852- BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \
877+ BOOLOP = BINOP = UNARYOP = IFEXP = SET = \
853878 COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = \
854879 STARRED = NAMECONSTANT = handleChildren
855880
@@ -870,6 +895,33 @@ def ignore(self, node):
870895 # additional node types
871896 COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren
872897
898+ def DICT (self , node ):
899+ keys = node .keys
900+ keys = [self .convert_to_value (key ) for key in keys ]
901+ for key in set (keys ):
902+ if keys .count (key ) > 1 :
903+ if isinstance (key , VariableKey ):
904+ self .report (messages .DuplicateVariableDictionaryKey ,
905+ node , key .name )
906+ else :
907+ self .report (messages .DuplicateDictionaryKey , node , key )
908+ self .handleChildren (node )
909+
910+ def convert_to_value (self , item ):
911+ if isinstance (item , ast .Str ):
912+ return item .s
913+ elif isinstance (item , ast .Tuple ):
914+ return tuple (self .convert_to_value (i ) for i in item .elts )
915+ elif isinstance (item , ast .Num ):
916+ return item .n
917+ elif isinstance (item , ast .Name ):
918+ return VariableKey (name = item .id )
919+ elif (not PY2 ) and isinstance (item , ast .NameConstant ):
920+ # None, True, False are nameconstants in python3, but names in 2
921+ return item .value
922+ else :
923+ return UnhandledKeyType ()
924+
873925 def ASSERT (self , node ):
874926 if isinstance (node .test , ast .Tuple ) and node .test .elts != []:
875927 self .report (messages .AssertTuple , node )
0 commit comments