void macroUsed()

in glean/lang/clang/preprocessor.cpp [104:237]


  void macroUsed(
      const clang::Token& name,
      const clang::MacroDefinition& def,
      clang::SourceRange range,
      bool expand) {

#define PROFILE_macroUsed 0

#if PROFILE_macroUsed
    using Clock = std::chrono::steady_clock;
    static std::chrono::microseconds time = std::chrono::microseconds::zero();
    static size_t count = 0;

    const auto start = Clock::now();
#endif


    // Getting the location is expensive and there are a lot fewer macro
    // definition sites than there are macro expansions so let's cache those
    // locations.
    folly::Optional<Src::Loc> defloc;
    if (auto info = def.getMacroInfo()) {
      defloc = folly::get_optional(macros, info);
      if (!defloc.has_value()) {
        defloc = db.srcLoc(info->getDefinitionLoc());
        macros.insert({info, defloc.value()});
      }
    }

    // We absolutely don't want to let Clang resolve nested macro expansion
    // ranges here (via db.srcRange -> getExpansionRange) as doing so turned out
    // to be horrendously expensive. Instead, when we see a top-level expansion
    // (the range isn't isMacroID) we resolve it ourselves and store it in
    // 'expansion'. Subsequent nested expansions must be part of this top-level
    // expansion so we just use that range. There is a subtlety with how we
    // handle expansions in macro arguments, see comments below. This is a
    // massive win in performance - at one time, this function accounted for
    // >40% of the running time in Strobelight (cf. T59197014).
    const ClangDB::SourceRange src =
      (range.getBegin().isMacroID() && expansion.has_value())

      // This is part of the current top-level expansion, just use its
      // range.
      ? expansion.value()

      // Manually convert this to a CharSourceRange and call the more
      // efficient immediateSourceRange rather than srcRange.
      : db.immediateSrcRange(
          clang::CharSourceRange::getCharRange(
            { // getBegin points at the start of the first token
              range.getBegin(),

              // getEnd points at the start of the last token.
              range.getEnd().getLocWithOffset(
                // Skip over the last token to make this an proper (exclusive)
                // char range.
                range.getBegin() == range.getEnd()
                  // The macro expansion range is only one token which must be
                  // the macro name.
                  ? name.getLength()

                  // Multiple tokens which means the last token must be the
                  // closing parenthesis.
                  : 1)
            }));

    if (range.getBegin().isMacroID() && !expansion.has_value()) {
      // This really shouldn't happen.
      LOG(ERROR)
        << "Unexpected nested macro expansion at "
        << range.printToString(db.sourceManager());
    }

    // Don't update expansion if this is an expansion of a macro argument.
    // Consider:
    //
    //   #define ONE 1
    //   #define TWO 2
    //   #define FOO ONE
    //   #define MACRO(x) x+TWO
    //   int y = MACRO(FOO);
    //
    // Here, we get the following calls:
    //
    //   MACRO(...) - the outer expansion (not isMacroID)
    //   FOO - argument (not isMacroID)
    //   ONE - definition of FOO (isMacroID)
    //   TWO - definition of MACRO(x) (isMacroID)
    //
    // We set expansion in the MACRO(...) call but if we then update it in
    // the FOO call, we'd assign the (nested) expansion of TWO to the range
    // of FOO. Instead, we don't update and assign the expansions of both
    // ONE and TWO (but not FOO!) to the range of MACRO(FOO). This is arguably
    // slightly less wrong. There doesn't seem to be an easy way to do better
    // without actually resolving the MacroID ranges. Perhaps we could hook
    // into the lexer somehow.
    if (!range.getBegin().isMacroID() &&
        (!expansion.has_value()
          || src.file != expansion->file
          || src.span.start + src.span.length
              > expansion->span.start + expansion->span.length)) {
      expansion = src;
    }

    // Resolve the name range manually, too. For top-level expansions, it's
    // the (inclusive for now) range of the name token. For nested expansions
    // the current schema is broken anyway - we assign the range of the
    // top-level expansion (it should be spelling file + range).
    const auto name_r = name.getLocation().isMacroID()
      ? src
      : db.immediateSrcRange(
          clang::CharSourceRange::getCharRange({
            name.getLocation(),
            name.getEndLoc()
          }));

    auto use = db.fact<Pp::Use>(
      macro(name),
      Src::ByteRange{name_r.span.start, name_r.span.start + name_r.span.length},
      maybe(defloc),
      expand,
      src.range);
    db.ppevent(Cxx::PPEvent::use(use), src);

#if PROFILE_macroUsed
    const auto end = Clock::now();

    time += std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    ++count;
    if ((count % 10000) == 0) {
      LOG(INFO) << "macroUsed " << time.count() << "us (" << count << ")";
    }
#endif
  }