docs: private AstNode propertyAccess(int tt, AstNode pn)
              throws IOException
      {
          if (pn == null) codeBug();
          int memberTypeFlags = 0, lineno = ts.lineno, dotPos = ts.tokenBeg;
          consumeToken();
  
          if (tt == Token.DOTDOT) {
              mustHaveXML();
              memberTypeFlags = Node.DESCENDANTS_FLAG;
          }
  
          if (!compilerEnv.isXmlAvailable()) {
              int maybeName = nextToken();
              if (maybeName != Token.NAME
                      && !(compilerEnv.isReservedKeywordAsIdentifier()
                      && TokenStream.isKeyword(ts.getString(), compilerEnv.getLanguageVersion(), inUseStrictDirective))) {
                reportError("msg.no.name.after.dot");
              }
  
              Name name = createNameNode(true, Token.GETPROP);
              PropertyGet pg = new PropertyGet(pn, name, dotPos);
              pg.setLineno(lineno);
              return pg;
          }
  
          AstNode ref = null;  // right side of . or .. operator
  
          int token = nextToken();
          switch (token) {
            case Token.THROW:
                // needed for generator.throw();
                saveNameTokenData(ts.tokenBeg, "throw", ts.lineno);
                ref = propertyName(-1, "throw", memberTypeFlags);
                break;
  
            case Token.NAME:
                // handles: name, ns::name, ns::*, ns::[expr]
                ref = propertyName(-1, ts.getString(), memberTypeFlags);
                break;
  
            case Token.MUL:
                // handles: *, *::name, *::*, *::[expr]
                saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
                ref = propertyName(-1, "*", memberTypeFlags);
                break;
  
            case Token.XMLATTR:
                // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
                //          '@::attr', '@::*', '@*', '@*::attr', '@*::*'
                ref = attributeAccess();
                break;
  
            case Token.RESERVED: {
                String name = ts.getString();
                saveNameTokenData(ts.tokenBeg, name, ts.lineno);
                ref = propertyName(-1, name, memberTypeFlags);
                break;
            }
  
            default:
                if (compilerEnv.isReservedKeywordAsIdentifier()) {
                    // allow keywords as property names, e.g. ({if: 1})
                    String name = Token.keywordToName(token);
                    if (name != null) {
                        saveNameTokenData(ts.tokenBeg, name, ts.lineno);
                        ref = propertyName(-1, name, memberTypeFlags);
                        break;
                    }
                }
                reportError("msg.no.name.after.dot");
                return makeErrorNode();
          }
  
          boolean xml = ref instanceof XmlRef;
          InfixExpression result = xml ? new XmlMemberGet() : new PropertyGet();
          if (xml && tt == Token.DOT)
              result.setType(Token.DOT);
          int pos = pn.getPosition();
          result.setPosition(pos);
          result.setLength(getNodeEnd(ref) - pos);
          result.setOperatorPosition(dotPos - pos);
          result.setLineno(pn.getLineno());
          result.setLeft(pn);  // do this after setting position
          result.setRight(ref);
          return result;
      }