private BitSet getParamUsage(PreprocessorMacro macro) {
    final BitSet result = new BitSet();
    final TokenList replacement = macro.getTokens(fDefinitionParser, fLexOptions, this);

    Token l = null;
    Token n;
    for (Token t = replacement.first(); t != null; l = t, t = n) {
      n = (Token) t.getNext();
      switch (t.getType()) {
        case ObjCPreprocessor.tMACRO_PARAMETER:
          int idx = 2 * ((TokenParameterReference) t).getIndex();
          if (!isKind(n, IToken.tPOUNDPOUND)) {
            idx++;
          }
          result.set(idx);
          break;

        case IToken.tPOUND:
          if (isKind(n, ObjCPreprocessor.tMACRO_PARAMETER)) {
            idx = ((TokenParameterReference) n).getIndex();
            result.set(2 * idx);
            t = n;
            n = (Token) n.getNext();
          }
          break;

        case IToken.tPOUNDPOUND:
          if (isKind(n, ObjCPreprocessor.tMACRO_PARAMETER)) {
            idx = ((TokenParameterReference) n).getIndex();
            // gcc-extension
            if (isKind(l, IToken.tCOMMA)
                && macro.hasVarArgs() != FunctionStyleMacro.NO_VAARGS
                && idx == macro.getParameterPlaceholderList().length - 1
                && !isKind(n.getNext(), IToken.tPOUNDPOUND)) {
              result.set(2 * idx + 1);
            } else {
              result.set(2 * idx);
            }
            t = n;
            n = (Token) n.getNext();
          }
          break;
      }
    }
    return result;
  }
  /**
   * Expects that the identifier of the macro expansion has been consumed. Expands the macro
   * consuming tokens from the input (to read the parameters) and stores the resulting tokens
   * together with boundary markers in the result token list. Returns the last token of the
   * expansion.
   *
   * @throws AbortMacroExpansionException
   */
  private Token expandOne(
      Token lastConsumed,
      PreprocessorMacro macro,
      IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden,
      TokenSource input,
      TokenList result,
      MacroExpansionTracker tracker)
      throws OffsetLimitReachedException {
    if (macro.isFunctionStyle()) {
      final int paramCount = macro.getParameterPlaceholderList().length;
      final TokenSource[] argInputs = new TokenSource[paramCount];
      final BitSet paramUsage = getParamUsage(macro);
      if (tracker != null) {
        tracker.startFunctionStyleMacro((Token) lastConsumed.clone());
      }
      try {
        lastConsumed =
            parseArguments(input, (FunctionStyleMacro) macro, forbidden, argInputs, tracker);
      } catch (AbortMacroExpansionException e) {
        // ignore this macro expansion
        for (int i = 0; i < argInputs.length; i++) {
          TokenSource argInput = argInputs[i];
          executeScopeMarkers(argInput, forbidden);
          if (tracker != null) {
            tracker.setExpandedMacroArgument(null);
          }
        }
        if (tracker != null) {
          if (tracker.isRequestedStep()) {
            tracker.storeFunctionStyleMacroReplacement(macro, new TokenList(), result);
          } else if (tracker.isDone()) {
            tracker.appendFunctionStyleMacro(result);
          }
          tracker.endFunctionStyleMacro();
        }
        return null;
      }

      TokenList[] clonedArgs = new TokenList[paramCount];
      TokenList[] expandedArgs = new TokenList[paramCount];
      for (int i = 0; i < paramCount; i++) {
        final TokenSource argInput = argInputs[i];
        final boolean needCopy = paramUsage.get(2 * i);
        final boolean needExpansion = paramUsage.get(2 * i + 1);
        clonedArgs[i] = needCopy ? argInput.cloneTokens() : EMPTY_TOKEN_LIST;
        expandedArgs[i] =
            needExpansion ? expandAll(argInput, forbidden, false, tracker) : EMPTY_TOKEN_LIST;
        if (!needExpansion) {
          executeScopeMarkers(argInput, forbidden);
        }

        if (tracker != null) {
          tracker.setExpandedMacroArgument(needExpansion ? expandedArgs[i] : null);
          // make sure that the trailing arguments do not get
          // expanded.
          if (tracker.isDone()) {
            paramUsage.clear();
          }
        }
      }
      if (tracker == null) {
        replaceArgs(macro, clonedArgs, expandedArgs, result);
      } else {
        if (tracker.isRequestedStep()) {
          TokenList replacement = new TokenList();
          replaceArgs(macro, clonedArgs, expandedArgs, replacement);
          tracker.storeFunctionStyleMacroReplacement(macro, replacement, result);
        } else if (tracker.isDone()) {
          tracker.appendFunctionStyleMacro(result);
        } else {
          replaceArgs(macro, clonedArgs, expandedArgs, result);
        }
        tracker.endFunctionStyleMacro();
      }
    } else {
      if (tracker == null) {
        objStyleTokenPaste(macro, result);
      } else {
        if (tracker.isRequestedStep()) {
          TokenList replacement = new TokenList();
          objStyleTokenPaste(macro, replacement);
          tracker.storeObjectStyleMacroReplacement(macro, lastConsumed, replacement, result);
        } else {
          objStyleTokenPaste(macro, result);
        }
        tracker.endObjectStyleMacro();
      }
    }
    return lastConsumed;
  }