summaryrefslogtreecommitdiffstats
path: root/clangd/refactor/Tweak.cpp
blob: 34634e64b6f972a7dc99b22de5b0ccf2fed9a96e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//===--- Tweak.cpp -----------------------------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Tweak.h"
#include "Logger.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Registry.h"
#include <functional>
#include <memory>

LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::clangd::Tweak>)

namespace clang {
namespace clangd {

/// A handy typedef to save some typing.
typedef llvm::Registry<Tweak> TweakRegistry;

namespace {
/// Asserts invariants on TweakRegistry. No-op with assertion disabled.
void validateRegistry() {
#ifndef NDEBUG
  llvm::StringSet<> Seen;
  for (const auto &E : TweakRegistry::entries()) {
    // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name.
    // We check that id() matches it.
    assert(E.instantiate()->id() == E.getName() &&
           "id should be equal to class name");
    assert(Seen.try_emplace(E.getName()).second && "duplicate check id");
  }
#endif
}
} // namespace

Tweak::Selection::Selection(ParsedAST &AST, unsigned RangeBegin,
                            unsigned RangeEnd)
    : AST(AST), ASTSelection(AST.getASTContext(), RangeBegin, RangeEnd) {
  auto &SM = AST.getASTContext().getSourceManager();
  Code = SM.getBufferData(SM.getMainFileID());
  Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin);
}

std::vector<std::unique_ptr<Tweak>> prepareTweaks(const Tweak::Selection &S) {
  validateRegistry();

  std::vector<std::unique_ptr<Tweak>> Available;
  for (const auto &E : TweakRegistry::entries()) {
    std::unique_ptr<Tweak> T = E.instantiate();
    if (!T->prepare(S))
      continue;
    Available.push_back(std::move(T));
  }
  // Ensure deterministic order of the results.
  llvm::sort(Available,
             [](const std::unique_ptr<Tweak> &L,
                const std::unique_ptr<Tweak> &R) { return L->id() < R->id(); });
  return Available;
}

llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(StringRef ID,
                                                    const Tweak::Selection &S) {
  auto It = llvm::find_if(
      TweakRegistry::entries(),
      [ID](const TweakRegistry::entry &E) { return E.getName() == ID; });
  if (It == TweakRegistry::end())
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                   "id of the tweak is invalid");
  std::unique_ptr<Tweak> T = It->instantiate();
  if (!T->prepare(S))
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                   "failed to prepare() a check");
  return std::move(T);
}

} // namespace clangd
} // namespace clang