Coverage Report

Created: 2022-07-16 07:03

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- ComputeReplacements.cpp --------------------------------*- C++ -*-=====//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
#include "clang/Tooling/Core/Replacement.h"
9
#include "clang/Tooling/Syntax/Mutations.h"
10
#include "clang/Tooling/Syntax/TokenBufferTokenManager.h"
11
#include "clang/Tooling/Syntax/Tokens.h"
12
#include "clang/Tooling/Syntax/Tree.h"
13
#include "llvm/Support/Error.h"
14
15
using namespace clang;
16
17
namespace {
18
using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,
19
                                                bool /*IsOriginal*/)>;
20
/// Enumerates spans of tokens from the tree consecutively laid out in memory.
21
void enumerateTokenSpans(const syntax::Tree *Root,
22
                         const syntax::TokenBufferTokenManager &STM,
23
42
                         ProcessTokensFn Callback) {
24
42
  struct Enumerator {
25
42
    Enumerator(const syntax::TokenBufferTokenManager &STM,
26
42
               ProcessTokensFn Callback)
27
42
        : STM(STM), SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
28
42
          Callback(Callback) {}
29
30
42
    void run(const syntax::Tree *Root) {
31
42
      process(Root);
32
      // Report the last span to the user.
33
42
      if (SpanBegin)
34
42
        Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
35
42
    }
36
37
42
  private:
38
798
    void process(const syntax::Node *N) {
39
798
      if (auto *T = dyn_cast<syntax::Tree>(N)) {
40
1.13k
        for (const auto *C = T->getFirstChild(); C != nullptr;
41
756
             C = C->getNextSibling())
42
756
          process(C);
43
378
        return;
44
378
      }
45
46
420
      auto *L = cast<syntax::Leaf>(N);
47
420
      if (SpanEnd == STM.getToken(L->getTokenKey()) &&
48
420
          
SpanIsOriginal == L->isOriginal()322
) {
49
        // Extend the current span.
50
322
        ++SpanEnd;
51
322
        return;
52
322
      }
53
      // Report the current span to the user.
54
98
      if (SpanBegin)
55
56
        Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
56
      // Start recording a new span.
57
98
      SpanBegin = STM.getToken(L->getTokenKey());
58
98
      SpanEnd = SpanBegin + 1;
59
98
      SpanIsOriginal = L->isOriginal();
60
98
    }
61
62
42
    const syntax::TokenBufferTokenManager &STM;
63
42
    const syntax::Token *SpanBegin;
64
42
    const syntax::Token *SpanEnd;
65
42
    bool SpanIsOriginal;
66
42
    ProcessTokensFn Callback;
67
42
  };
68
69
42
  return Enumerator(STM, Callback).run(Root);
70
42
}
71
72
syntax::FileRange rangeOfExpanded(const syntax::TokenBufferTokenManager &STM,
73
42
                                  llvm::ArrayRef<syntax::Token> Expanded) {
74
42
  const auto &Buffer = STM.tokenBuffer();
75
42
  const auto &SM = STM.sourceManager();
76
77
  // Check that \p Expanded actually points into expanded tokens.
78
42
  assert(Buffer.expandedTokens().begin() <= Expanded.begin());
79
0
  assert(Expanded.end() < Buffer.expandedTokens().end());
80
81
42
  if (Expanded.empty())
82
    // (!) empty tokens must always point before end().
83
0
    return syntax::FileRange(
84
0
        SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);
85
86
42
  auto Spelled = Buffer.spelledForExpanded(Expanded);
87
42
  assert(Spelled && "could not find spelled tokens for expanded");
88
0
  return syntax::Token::range(SM, Spelled->front(), Spelled->back());
89
42
}
90
} // namespace
91
92
tooling::Replacements
93
syntax::computeReplacements(const TokenBufferTokenManager &TBTM,
94
42
                            const syntax::TranslationUnit &TU) {
95
42
  const auto &Buffer = TBTM.tokenBuffer();
96
42
  const auto &SM = TBTM.sourceManager();
97
98
42
  tooling::Replacements Replacements;
99
  // Text inserted by the replacement we are building now.
100
42
  std::string Replacement;
101
126
  auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {
102
126
    if (ReplacedRange.empty() && 
Replacement.empty()84
)
103
84
      return;
104
42
    llvm::cantFail(Replacements.add(tooling::Replacement(
105
42
        SM, rangeOfExpanded(TBTM, ReplacedRange).toCharRange(SM),
106
42
        Replacement)));
107
42
    Replacement = "";
108
42
  };
109
42
  const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();
110
42
  enumerateTokenSpans(
111
98
      &TU, TBTM, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {
112
98
        if (!IsOriginal) {
113
14
          Replacement +=
114
14
              syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);
115
14
          return;
116
14
        }
117
84
        assert(NextOriginal <= Tokens.begin());
118
        // We are looking at a span of original tokens.
119
84
        if (NextOriginal != Tokens.begin()) {
120
          // There is a gap, record a replacement or deletion.
121
42
          emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin()));
122
42
        } else {
123
          // No gap, but we may have pending insertions. Emit them now.
124
42
          emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/0));
125
42
        }
126
84
        NextOriginal = Tokens.end();
127
84
      });
128
129
  // We might have pending replacements at the end of file. If so, emit them.
130
42
  emitReplacement(llvm::makeArrayRef(
131
42
      NextOriginal, Buffer.expandedTokens().drop_back().end()));
132
133
42
  return Replacements;
134
42
}