@@ -469,6 +469,7 @@ private void traverseNode(Node n, Scope scope) {
469469 break ;
470470
471471 case CALL :
472+ case OPTCHAIN_CALL :
472473 traverseCall (n , scope );
473474 break ;
474475
@@ -546,7 +547,8 @@ private void traverseNode(Node n, Scope scope) {
546547 break ;
547548
548549 case GETPROP :
549- traverseGetProp (n , scope );
550+ case OPTCHAIN_GETPROP :
551+ traverseNormalOrOptChainGetProp (n , scope );
550552 break ;
551553
552554 default :
@@ -569,7 +571,17 @@ private void traverseInstanceof(Node instanceofNode, Scope scope) {
569571 }
570572 }
571573
572- private void traverseGetProp (Node getProp , Scope scope ) {
574+ /**
575+ * Traverse `expr.prop` or `expr?.prop`.
576+ *
577+ * <p>Note that this method is called only for RHS nodes. Property references that are being
578+ * assigned to are handled by the logic traversing their parent (e.g. ASSIGN) node.
579+ *
580+ * <p>The primary purpose of this method is to make sure the property reference is correctly
581+ * recorded.
582+ */
583+ private void traverseNormalOrOptChainGetProp (Node getProp , Scope scope ) {
584+ checkState (NodeUtil .isNormalOrOptChainGetProp (getProp ), getProp );
573585 Node objectNode = getProp .getFirstChild ();
574586 Node propertyNameNode = objectNode .getNext ();
575587 String propertyName = propertyNameNode .getString ();
@@ -586,7 +598,10 @@ private void traverseGetProp(Node getProp, Scope scope) {
586598 markPropertyNameAsPinned (propertyName );
587599 traverseNode (objectNode , scope );
588600 } else if (objectNode .isThis ()) {
601+ // This is probably the declaration of a class field in a constructor.
602+ // /** @private {number} */
589603 // this.propName;
604+ // We don't want to consider this a real usage that should prevent removal.
590605 RemovableBuilder builder = new RemovableBuilder ().setIsThisDotPropertyReference (true );
591606 considerForIndependentRemoval (builder .buildUnusedReadReference (getProp , propertyNameNode ));
592607 } else if (isDotPrototype (objectNode )) {
@@ -909,7 +924,8 @@ private boolean isAssignmentToPrototype(Node n) {
909924
910925 /** True for `someExpression.prototype`. */
911926 private static boolean isDotPrototype (Node n ) {
912- return n .isGetProp () && n .getLastChild ().getString ().equals ("prototype" );
927+ return NodeUtil .isNormalOrOptChainGetProp (n )
928+ && n .getLastChild ().getString ().equals ("prototype" );
913929 }
914930
915931 private void traverseCatch (Node catchNode , Scope scope ) {
@@ -1463,8 +1479,8 @@ private void considerForIndependentRemoval(Removable removable) {
14631479
14641480 /** @return Whether or not accessor side-effect are a possibility. */
14651481 private boolean considerForAccessorSideEffects (Node getprop , PropertyAccessKind usage ) {
1466- checkState ( getprop . isGetProp (), getprop ); // Other node types may make sense in the future.
1467-
1482+ // Other node types may make sense in the future.
1483+ checkState ( NodeUtil . isNormalOrOptChainGetProp ( getprop ), getprop );
14681484 String propName = getprop .getSecondChild ().getString ();
14691485 PropertyAccessKind recorded = compiler .getAccessorSummary ().getKind (propName );
14701486 if ((recorded .hasGetter () && usage .hasGetter () && !assumeGettersArePure )
@@ -2072,12 +2088,12 @@ public String toString() {
20722088
20732089 /** True for `this.propertyName` */
20742090 private static boolean isThisDotProperty (Node n ) {
2075- return n . isGetProp ( ) && n .getFirstChild ().isThis ();
2091+ return NodeUtil . isNormalOrOptChainGetProp ( n ) && n .getFirstChild ().isThis ();
20762092 }
20772093
20782094 /** True for `(something).prototype.propertyName` */
20792095 private static boolean isDotPrototypeDotProperty (Node n ) {
2080- return n . isGetProp ( ) && isDotPrototype (n .getFirstChild ());
2096+ return NodeUtil . isNormalOrOptChainGetProp ( n ) && isDotPrototype (n .getFirstChild ());
20812097 }
20822098
20832099 private class IndirectAssign extends Removable {
@@ -2854,7 +2870,7 @@ void considerPossibleReferenceInternal(Node possiblyReferencingNode) {
28542870 // A matching NAME node must be a reference (there's no need to check that the referenced
28552871 // Var is global, since local variables have all been renamed by normalization).
28562872 isRemovable = false ;
2857- } else if (possiblyReferencingNode . isGetProp ( )) {
2873+ } else if (NodeUtil . isNormalOrOptChainGetProp ( possiblyReferencingNode )) {
28582874 // Assume that the owner is possibly the global `this` and skip removal.
28592875 isRemovable = false ;
28602876 }
@@ -2877,10 +2893,9 @@ String getName() {
28772893
28782894 @ Override
28792895 void considerPossibleReferenceInternal (Node possiblyReferencingNode ) {
2880- if (! possiblyReferencingNode . isGetProp ( )) {
2881- return ;
2896+ if (NodeUtil . isNormalOrOptChainGetProp ( possiblyReferencingNode )) {
2897+ isRemovable = false ;
28822898 }
2883- isRemovable = false ;
28842899 }
28852900 }
28862901
@@ -2900,11 +2915,10 @@ String getName() {
29002915
29012916 @ Override
29022917 void considerPossibleReferenceInternal (Node possiblyReferencingNode ) {
2903- if (!possiblyReferencingNode .isGetProp ()) {
2904- return ;
2918+ if (NodeUtil .isNormalOrOptChainGetProp (possiblyReferencingNode )) {
2919+ // Prototype properties are simply not removable.
2920+ isRemovable = false ;
29052921 }
2906- // Prototype properties are simply not removable.
2907- isRemovable = false ;
29082922 }
29092923 }
29102924
0 commit comments