Coverage Report

Created: 2020-09-22 08:39

/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/Tokens.h"
11
#include "llvm/Support/Error.h"
12
13
using namespace clang;
14
15
namespace {
16
using ProcessTokensFn = llvm::function_ref<void(llvm::ArrayRef<syntax::Token>,
17
                                                bool /*IsOriginal*/)>;
18
/// Enumerates spans of tokens from the tree consecutively laid out in memory.
19
42
void enumerateTokenSpans(const syntax::Tree *Root, ProcessTokensFn Callback) {
20
42
  struct Enumerator {
21
42
    Enumerator(ProcessTokensFn Callback)
22
42
        : SpanBegin(nullptr), SpanEnd(nullptr), SpanIsOriginal(false),
23
42
          Callback(Callback) {}
24
25
42
    void run(const syntax::Tree *Root) {
26
42
      process(Root);
27
      // Report the last span to the user.
28
42
      if (SpanBegin)
29
42
        Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
30
42
    }
31
32
42
  private:
33
742
    void process(const syntax::Node *N) {
34
742
      if (auto *T = dyn_cast<syntax::Tree>(N)) {
35
1.02k
        for (const auto *C = T->getFirstChild(); C != nullptr;
36
700
             C = C->getNextSibling())
37
700
          process(C);
38
322
        return;
39
322
      }
40
41
420
      auto *L = cast<syntax::Leaf>(N);
42
420
      if (SpanEnd == L->getToken() && 
SpanIsOriginal == L->isOriginal()322
) {
43
        // Extend the current span.
44
322
        ++SpanEnd;
45
322
        return;
46
322
      }
47
      // Report the current span to the user.
48
98
      if (SpanBegin)
49
56
        Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
50
      // Start recording a new span.
51
98
      SpanBegin = L->getToken();
52
98
      SpanEnd = SpanBegin + 1;
53
98
      SpanIsOriginal = L->isOriginal();
54
98
    }
55
56
42
    const syntax::Token *SpanBegin;
57
42
    const syntax::Token *SpanEnd;
58
42
    bool SpanIsOriginal;
59
42
    ProcessTokensFn Callback;
60
42
  };
61
62
42
  return Enumerator(Callback).run(Root);
63
42
}
64
65
syntax::FileRange rangeOfExpanded(const syntax::Arena &A,
66
42
                                  llvm::ArrayRef<syntax::Token> Expanded) {
67
42
  const auto &Buffer = A.getTokenBuffer();
68
42
  const auto &SM = A.getSourceManager();
69
70
  // Check that \p Expanded actually points into expanded tokens.
71
42
  assert(Buffer.expandedTokens().begin() <= Expanded.begin());
72
42
  assert(Expanded.end() < Buffer.expandedTokens().end());
73
74
42
  if (Expanded.empty())
75
    // (!) empty tokens must always point before end().
76
0
    return syntax::FileRange(
77
0
        SM, SM.getExpansionLoc(Expanded.begin()->location()), /*Length=*/0);
78
79
42
  auto Spelled = Buffer.spelledForExpanded(Expanded);
80
42
  assert(Spelled && "could not find spelled tokens for expanded");
81
42
  return syntax::Token::range(SM, Spelled->front(), Spelled->back());
82
42
}
83
} // namespace
84
85
tooling::Replacements
86
syntax::computeReplacements(const syntax::Arena &A,
87
42
                            const syntax::TranslationUnit &TU) {
88
42
  const auto &Buffer = A.getTokenBuffer();
89
42
  const auto &SM = A.getSourceManager();
90
91
42
  tooling::Replacements Replacements;
92
  // Text inserted by the replacement we are building now.
93
42
  std::string Replacement;
94
126
  auto emitReplacement = [&](llvm::ArrayRef<syntax::Token> ReplacedRange) {
95
126
    if (ReplacedRange.empty() && 
Replacement.empty()84
)
96
84
      return;
97
42
    llvm::cantFail(Replacements.add(tooling::Replacement(
98
42
        SM, rangeOfExpanded(A, ReplacedRange).toCharRange(SM), Replacement)));
99
42
    Replacement = "";
100
42
  };
101
102
42
  const syntax::Token *NextOriginal = Buffer.expandedTokens().begin();
103
42
  enumerateTokenSpans(
104
98
      &TU, [&](llvm::ArrayRef<syntax::Token> Tokens, bool IsOriginal) {
105
98
        if (!IsOriginal) {
106
14
          Replacement +=
107
14
              syntax::Token::range(SM, Tokens.front(), Tokens.back()).text(SM);
108
14
          return;
109
14
        }
110
84
        assert(NextOriginal <= Tokens.begin());
111
        // We are looking at a span of original tokens.
112
84
        if (NextOriginal != Tokens.begin()) {
113
          // There is a gap, record a replacement or deletion.
114
42
          emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin()));
115
42
        } else {
116
          // No gap, but we may have pending insertions. Emit them now.
117
42
          emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/0));
118
42
        }
119
84
        NextOriginal = Tokens.end();
120
84
      });
121
122
  // We might have pending replacements at the end of file. If so, emit them.
123
42
  emitReplacement(llvm::makeArrayRef(
124
42
      NextOriginal, Buffer.expandedTokens().drop_back().end()));
125
126
42
  return Replacements;
127
42
}