Coverage Report

Created: 2020-02-15 09:57

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