brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.2 KiB · 29e681c Raw
137 lines · cpp
1//===--- ExpandMacro.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "refactor/Tweak.h"10#include "clang/Basic/SourceLocation.h"11#include "clang/Basic/SourceManager.h"12#include "clang/Basic/TokenKinds.h"13#include "clang/Tooling/Core/Replacement.h"14#include "clang/Tooling/Syntax/Tokens.h"15#include "llvm/ADT/ArrayRef.h"16#include "llvm/ADT/STLExtras.h"17#include "llvm/Support/Error.h"18#include <string>19namespace clang {20namespace clangd {21namespace {22 23/// Replaces a reference to a macro under the cursor with its expansion.24/// Before:25///   #define FOO(X) X+X26///   FOO(10*a)27///   ^^^28/// After:29///   #define FOO(X) X+X30///   10*a+10*a31class ExpandMacro : public Tweak {32public:33  const char *id() const final;34  llvm::StringLiteral kind() const override {35    return CodeAction::REFACTOR_KIND;36  }37 38  bool prepare(const Selection &Inputs) override;39  Expected<Tweak::Effect> apply(const Selection &Inputs) override;40  std::string title() const override;41 42private:43  syntax::TokenBuffer::Expansion Expansion;44  std::string MacroName;45};46 47REGISTER_TWEAK(ExpandMacro)48 49/// Finds a spelled token that the cursor is pointing at.50static const syntax::Token *51findTokenUnderCursor(const SourceManager &SM,52                     llvm::ArrayRef<syntax::Token> Spelled,53                     unsigned CursorOffset) {54  // Find the token that strats after the offset, then look at a previous one.55  auto *It = llvm::partition_point(Spelled, [&](const syntax::Token &T) {56    assert(T.location().isFileID());57    return SM.getFileOffset(T.location()) <= CursorOffset;58  });59  if (It == Spelled.begin())60    return nullptr;61  // Check the token we found actually touches the cursor position.62  --It;63  return It->range(SM).touches(CursorOffset) ? It : nullptr;64}65 66static const syntax::Token *67findIdentifierUnderCursor(const syntax::TokenBuffer &Tokens,68                          SourceLocation Cursor) {69  assert(Cursor.isFileID());70 71  auto &SM = Tokens.sourceManager();72  auto Spelled = Tokens.spelledTokens(SM.getFileID(Cursor));73 74  auto *T = findTokenUnderCursor(SM, Spelled, SM.getFileOffset(Cursor));75  if (!T)76    return nullptr;77  if (T->kind() == tok::identifier)78    return T;79  // Also try the previous token when the cursor is at the boundary, e.g.80  //   FOO^()81  //   FOO^+82  if (T == Spelled.begin())83    return nullptr;84  --T;85  if (T->endLocation() != Cursor || T->kind() != tok::identifier)86    return nullptr;87  return T;88}89 90bool ExpandMacro::prepare(const Selection &Inputs) {91  // FIXME: we currently succeed on selection at the end of the token, e.g.92  //        'FOO[[ ]]BAR'. We should not trigger in that case.93 94  // Find a token under the cursor.95  auto *T = findIdentifierUnderCursor(Inputs.AST->getTokens(), Inputs.Cursor);96  // We are interested only in identifiers, other tokens can't be macro names.97  if (!T)98    return false;99  // If the identifier is a macro we will find the corresponding expansion.100  auto Expansion = Inputs.AST->getTokens().expansionStartingAt(T);101  if (!Expansion)102    return false;103  this->MacroName = std::string(T->text(Inputs.AST->getSourceManager()));104  this->Expansion = *Expansion;105  return true;106}107 108Expected<Tweak::Effect> ExpandMacro::apply(const Selection &Inputs) {109  auto &SM = Inputs.AST->getSourceManager();110 111  std::string Replacement;112  for (const syntax::Token &T : Expansion.Expanded) {113    Replacement += T.text(SM);114    Replacement += " ";115  }116  if (!Replacement.empty()) {117    assert(Replacement.back() == ' ');118    Replacement.pop_back();119  }120 121  CharSourceRange MacroRange =122      CharSourceRange::getCharRange(Expansion.Spelled.front().location(),123                                    Expansion.Spelled.back().endLocation());124 125  tooling::Replacements Reps;126  llvm::cantFail(Reps.add(tooling::Replacement(SM, MacroRange, Replacement)));127  return Effect::mainFileEdit(SM, std::move(Reps));128}129 130std::string ExpandMacro::title() const {131  return std::string(llvm::formatv("Expand macro '{0}'", MacroName));132}133 134} // namespace135} // namespace clangd136} // namespace clang137