@@ -144,13 +144,13 @@ module.exports = function(context) {
144144 /**
145145 * Checks if the prop is declared
146146 * @param {Object } component The component to process
147- * @param {String } name Dot separated name of the prop to check.
147+ * @param {String[] } names List of names of the prop to check.
148148 * @returns {Boolean } True if the prop is declared, false if not.
149149 */
150- function isDeclaredInComponent ( component , name ) {
150+ function isDeclaredInComponent ( component , names ) {
151151 return _isDeclaredInComponent (
152152 component . declaredPropTypes || { } ,
153- name . split ( '.' )
153+ names
154154 ) ;
155155 }
156156
@@ -164,14 +164,14 @@ module.exports = function(context) {
164164 return tokens . length && tokens [ 0 ] . value === '...' ;
165165 }
166166
167+ /**
168+ * Retrieve the name of a key node
169+ * @param {ASTNode } node The AST node with the key.
170+ * @return {string } the name of the key
171+ */
167172 function getKeyValue ( node ) {
168173 var key = node . key ;
169- if ( key ) {
170- if ( key . type === 'Identifier' ) {
171- return key . name ;
172- }
173- return key . value ;
174- }
174+ return key . type === 'Identifier' ? key . name : key . value ;
175175 }
176176
177177 /**
@@ -321,22 +321,51 @@ module.exports = function(context) {
321321 return true ;
322322 }
323323
324+ /**
325+ * Retrieve the name of a property node
326+ * @param {ASTNode } node The AST node with the property.
327+ * @return {string } the name of the property or undefined if not found
328+ */
329+ function getPropertyName ( node ) {
330+ var property = node . property ;
331+ if ( property ) {
332+ switch ( property . type ) {
333+ case 'Identifier' :
334+ if ( node . computed ) {
335+ return '__COMPUTED_PROP__' ;
336+ }
337+ return property . name ;
338+ case 'Literal' :
339+ // Accept computed properties that are literal strings
340+ if ( typeof property . value === 'string' ) {
341+ return property . value ;
342+ }
343+ // falls through
344+ default :
345+ if ( node . computed ) {
346+ return '__COMPUTED_PROP__' ;
347+ }
348+ break ;
349+ }
350+ }
351+ }
352+
324353 /**
325354 * Mark a prop type as used
326355 * @param {ASTNode } node The AST node being marked.
327356 */
328- function markPropTypesAsUsed ( node , parentName ) {
357+ function markPropTypesAsUsed ( node , parentNames ) {
358+ parentNames = parentNames || [ ] ;
329359 var type ;
330- var name = node . parent . computed ?
331- '__COMPUTED_PROP__'
332- : node . parent . property && node . parent . property . name ;
333- var fullName = parentName ? parentName + '.' + name : name ;
334-
335- if ( node . parent . type === 'MemberExpression' ) {
336- markPropTypesAsUsed ( node . parent , fullName ) ;
337- }
338- if ( name && ! node . parent . computed ) {
339- type = 'direct' ;
360+ var name = getPropertyName ( node . parent ) ;
361+ var allNames ;
362+ if ( name ) {
363+ allNames = parentNames . concat ( name ) ;
364+ if ( node . parent . type === 'MemberExpression' ) {
365+ markPropTypesAsUsed ( node . parent , allNames ) ;
366+ }
367+ // Do not mark computed props as used.
368+ type = name !== '__COMPUTED_PROP__' ? 'direct' : null ;
340369 } else if (
341370 node . parent . parent . declarations &&
342371 node . parent . parent . declarations [ 0 ] . id . properties &&
@@ -354,7 +383,8 @@ module.exports = function(context) {
354383 break ;
355384 }
356385 usedPropTypes . push ( {
357- name : fullName ,
386+ name : name ,
387+ allNames : allNames ,
358388 node : node . parent . property
359389 } ) ;
360390 break ;
@@ -368,6 +398,7 @@ module.exports = function(context) {
368398 if ( propName ) {
369399 usedPropTypes . push ( {
370400 name : propName ,
401+ allNames : [ propName ] ,
371402 node : properties [ i ]
372403 } ) ;
373404 }
@@ -441,19 +472,20 @@ module.exports = function(context) {
441472 * @param {Object } component The component to process
442473 */
443474 function reportUndeclaredPropTypes ( component ) {
444- var name ;
475+ var allNames , name ;
445476 for ( var i = 0 , j = component . usedPropTypes . length ; i < j ; i ++ ) {
446477 name = component . usedPropTypes [ i ] . name ;
478+ allNames = component . usedPropTypes [ i ] . allNames ;
447479 if (
448- isIgnored ( name . split ( '.' ) . pop ( ) ) ||
449- isDeclaredInComponent ( component , name )
480+ isIgnored ( name ) ||
481+ isDeclaredInComponent ( component , allNames )
450482 ) {
451483 continue ;
452484 }
453485 context . report (
454486 component . usedPropTypes [ i ] . node ,
455487 component . name === componentUtil . DEFAULT_COMPONENT_NAME ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP , {
456- name : name . replace ( / \. _ _ C O M P U T E D _ P R O P _ _ / g, '[]' ) ,
488+ name : allNames . join ( '.' ) . replace ( / \. _ _ C O M P U T E D _ P R O P _ _ / g, '[]' ) ,
457489 component : component . name
458490 }
459491 ) ;
0 commit comments