clang-tools  16.0.0git
InsertionPoint.cpp
Go to the documentation of this file.
1 //===--- InsertionPoint.cpp - Where should we add new code? ---------------===//
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 
10 #include "support/Logger.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/DeclCXX.h"
13 #include "clang/AST/DeclObjC.h"
14 #include "clang/AST/DeclTemplate.h"
15 #include "clang/Basic/SourceManager.h"
16 
17 namespace clang {
18 namespace clangd {
19 namespace {
20 
21 // Choose the decl to insert before, according to an anchor.
22 // Nullptr means insert at end of DC.
23 // None means no valid place to insert.
24 llvm::Optional<const Decl *> insertionDecl(const DeclContext &DC,
25  const Anchor &A) {
26  bool LastMatched = false;
27  bool ReturnNext = false;
28  for (const auto *D : DC.decls()) {
29  if (D->isImplicit())
30  continue;
31  if (ReturnNext)
32  return D;
33 
34  const Decl *NonTemplate = D;
35  if (auto *TD = llvm::dyn_cast<TemplateDecl>(D))
36  NonTemplate = TD->getTemplatedDecl();
37  bool Matches = A.Match(NonTemplate);
38  dlog(" {0} {1} {2}", Matches, D->getDeclKindName(), D);
39 
40  switch (A.Direction) {
41  case Anchor::Above:
42  if (Matches && !LastMatched) {
43  // Special case: if "above" matches an access specifier, we actually
44  // want to insert below it!
45  if (llvm::isa<AccessSpecDecl>(D)) {
46  ReturnNext = true;
47  continue;
48  }
49  return D;
50  }
51  break;
52  case Anchor::Below:
53  if (LastMatched && !Matches)
54  return D;
55  break;
56  }
57 
58  LastMatched = Matches;
59  }
60  if (ReturnNext || (LastMatched && A.Direction == Anchor::Below))
61  return nullptr;
62  return llvm::None;
63 }
64 
65 SourceLocation beginLoc(const Decl &D) {
66  auto Loc = D.getBeginLoc();
67  if (RawComment *Comment = D.getASTContext().getRawCommentForDeclNoCache(&D)) {
68  auto CommentLoc = Comment->getBeginLoc();
69  if (CommentLoc.isValid() && Loc.isValid() &&
70  D.getASTContext().getSourceManager().isBeforeInTranslationUnit(
71  CommentLoc, Loc))
72  Loc = CommentLoc;
73  }
74  return Loc;
75 }
76 
77 bool any(const Decl *D) { return true; }
78 
79 SourceLocation endLoc(const DeclContext &DC) {
80  const Decl *D = llvm::cast<Decl>(&DC);
81  if (auto *OCD = llvm::dyn_cast<ObjCContainerDecl>(D))
82  return OCD->getAtEndRange().getBegin();
83  return D->getEndLoc();
84 }
85 
86 AccessSpecifier getAccessAtEnd(const CXXRecordDecl &C) {
87  AccessSpecifier Spec = (C.getTagKind() == TTK_Class ? AS_private : AS_public);
88  for (const auto *D : C.decls())
89  if (const auto *ASD = llvm::dyn_cast<AccessSpecDecl>(D))
90  Spec = ASD->getAccess();
91  return Spec;
92 }
93 
94 } // namespace
95 
96 SourceLocation insertionPoint(const DeclContext &DC,
97  llvm::ArrayRef<Anchor> Anchors) {
98  dlog("Looking for insertion point in {0}", DC.getDeclKindName());
99  for (const auto &A : Anchors) {
100  dlog(" anchor ({0})", A.Direction == Anchor::Above ? "above" : "below");
101  if (auto D = insertionDecl(DC, A)) {
102  dlog(" anchor matched before {0}", *D);
103  return *D ? beginLoc(**D) : endLoc(DC);
104  }
105  }
106  dlog("no anchor matched");
107  return SourceLocation();
108 }
109 
110 llvm::Expected<tooling::Replacement>
111 insertDecl(llvm::StringRef Code, const DeclContext &DC,
112  llvm::ArrayRef<Anchor> Anchors) {
113  auto Loc = insertionPoint(DC, Anchors);
114  // Fallback: insert at the end.
115  if (Loc.isInvalid())
116  Loc = endLoc(DC);
117  const auto &SM = DC.getParentASTContext().getSourceManager();
118  if (!SM.isWrittenInSameFile(Loc, cast<Decl>(DC).getLocation()))
119  return error("{0} body in wrong file: {1}", DC.getDeclKindName(),
120  Loc.printToString(SM));
121  return tooling::Replacement(SM, Loc, 0, Code);
122 }
123 
124 SourceLocation insertionPoint(const CXXRecordDecl &InClass,
125  std::vector<Anchor> Anchors,
126  AccessSpecifier Protection) {
127  for (auto &A : Anchors)
128  A.Match = [Inner(std::move(A.Match)), Protection](const Decl *D) {
129  return D->getAccess() == Protection && Inner(D);
130  };
131  return insertionPoint(InClass, Anchors);
132 }
133 
134 llvm::Expected<tooling::Replacement> insertDecl(llvm::StringRef Code,
135  const CXXRecordDecl &InClass,
136  std::vector<Anchor> Anchors,
137  AccessSpecifier Protection) {
138  // Fallback: insert at the bottom of the relevant access section.
139  Anchors.push_back({any, Anchor::Below});
140  auto Loc = insertionPoint(InClass, std::move(Anchors), Protection);
141  std::string CodeBuffer;
142  auto &SM = InClass.getASTContext().getSourceManager();
143  // Fallback: insert at the end of the class. Check if protection matches!
144  if (Loc.isInvalid()) {
145  Loc = InClass.getBraceRange().getEnd();
146  if (Protection != getAccessAtEnd(InClass)) {
147  CodeBuffer = (getAccessSpelling(Protection) + ":\n" + Code).str();
148  Code = CodeBuffer;
149  }
150  }
151  if (!SM.isWrittenInSameFile(Loc, InClass.getLocation()))
152  return error("Class body in wrong file: {0}", Loc.printToString(SM));
153  return tooling::Replacement(SM, Loc, 0, Code);
154 }
155 
156 } // namespace clangd
157 } // namespace clang
dlog
#define dlog(...)
Definition: Logger.h:101
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::clangd::Anchor::Below
@ Below
Definition: InsertionPoint.h:25
clang::clangd::insertDecl
llvm::Expected< tooling::Replacement > insertDecl(llvm::StringRef Code, const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
Definition: InsertionPoint.cpp:111
clang::clangd::error
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:79
clang::clangd::insertionPoint
SourceLocation insertionPoint(const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
Definition: InsertionPoint.cpp:96
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
Code
std::string Code
Definition: FindTargetTests.cpp:67
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
Inner
std::pair< Context, Canceler > Inner
Definition: CancellationTests.cpp:48
Logger.h
InsertionPoint.h
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Anchor::Above
@ Above
Definition: InsertionPoint.h:25