@ Override public void visit ( final NodeTraversal t , final Node n , final Node p ) { final JSDocInfo info = n . getJSDocInfo ( ) ; if ( info == null ) { return ; } final JSTypeRegistry registry = compiler . getTypeRegistry ( ) ; final List < Node > thrownTypes = transform ( info . getThrownTypes ( ) , new Function < JSTypeExpression , Node > ( ) { @ Override public Node apply ( JSTypeExpression expr ) { return expr . getRoot ( ) ; } } ) ; final Scope scope = t . getScope ( ) ; for ( Node typeRoot : info . getTypeNodes ( ) ) { NodeUtil . visitPreOrder ( typeRoot , new NodeUtil . Visitor ( ) { @ Override public void visit ( Node node ) { if ( ! node . isString ( ) ) { return ; } if ( thrownTypes . contains ( node ) ) { return ; } Node parent = node . getParent ( ) ; if ( parent != null ) { switch ( parent . getToken ( ) ) { case BANG : case QMARK : case THIS : // The names inside function(this:Foo) and case NEW : // function(new:Bar) are already non-null. case TYPEOF : // Names after 'typeof' don't have nullability. return ; case PIPE : { // Inside a union Node gp = parent . getParent ( ) ; if ( gp != null && gp . getToken ( ) == Token . QMARK ) { return ; // Inside an explicitly nullable union } for ( Node child : parent . children ( ) ) { if ( ( child . isString ( ) && child . getString ( ) . equals ( "null" ) ) || child . getToken ( ) == Token . QMARK ) { return ; // Inside a union that contains null or nullable type } } break ; } default : break ; } } String typeName = node . getString ( ) ; if ( typeName . equals ( "null" ) || registry . getType ( scope , typeName ) == null ) { return ; } JSType type = registry . createTypeFromCommentNode ( node ) ; if ( type . isNullable ( ) ) { compiler . report ( JSError . make ( node , IMPLICITLY_NULLABLE_JSDOC , typeName ) ) ; } } } , Predicates . alwaysTrue ( ) ) ; } }
Crawls the JSDoc of the given node to find any names in JSDoc that are implicitly null .