forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInsertionPoint.cpp
157 lines (141 loc) · 5.27 KB
/
InsertionPoint.cpp
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//===--- InsertionPoint.cpp - Where should we add new code? ---------------===//
//
// 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 "refactor/InsertionPoint.h"
#include "support/Logger.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/SourceManager.h"
namespace clang {
namespace clangd {
namespace {
// Choose the decl to insert before, according to an anchor.
// Nullptr means insert at end of DC.
// None means no valid place to insert.
llvm::Optional<const Decl *> insertionDecl(const DeclContext &DC,
const Anchor &A) {
bool LastMatched = false;
bool ReturnNext = false;
for (const auto *D : DC.decls()) {
if (D->isImplicit())
continue;
if (ReturnNext)
return D;
const Decl *NonTemplate = D;
if (auto *TD = llvm::dyn_cast<TemplateDecl>(D))
NonTemplate = TD->getTemplatedDecl();
bool Matches = A.Match(NonTemplate);
dlog(" {0} {1} {2}", Matches, D->getDeclKindName(), D);
switch (A.Direction) {
case Anchor::Above:
if (Matches && !LastMatched) {
// Special case: if "above" matches an access specifier, we actually
// want to insert below it!
if (llvm::isa<AccessSpecDecl>(D)) {
ReturnNext = true;
continue;
}
return D;
}
break;
case Anchor::Below:
if (LastMatched && !Matches)
return D;
break;
}
LastMatched = Matches;
}
if (ReturnNext || (LastMatched && A.Direction == Anchor::Below))
return nullptr;
return llvm::None;
}
SourceLocation beginLoc(const Decl &D) {
auto Loc = D.getBeginLoc();
if (RawComment *Comment = D.getASTContext().getRawCommentForDeclNoCache(&D)) {
auto CommentLoc = Comment->getBeginLoc();
if (CommentLoc.isValid() && Loc.isValid() &&
D.getASTContext().getSourceManager().isBeforeInTranslationUnit(
CommentLoc, Loc))
Loc = CommentLoc;
}
return Loc;
}
bool any(const Decl *D) { return true; }
SourceLocation endLoc(const DeclContext &DC) {
const Decl *D = llvm::cast<Decl>(&DC);
if (auto *OCD = llvm::dyn_cast<ObjCContainerDecl>(D))
return OCD->getAtEndRange().getBegin();
return D->getEndLoc();
}
AccessSpecifier getAccessAtEnd(const CXXRecordDecl &C) {
AccessSpecifier Spec = (C.getTagKind() == TTK_Class ? AS_private : AS_public);
for (const auto *D : C.decls())
if (const auto *ASD = llvm::dyn_cast<AccessSpecDecl>(D))
Spec = ASD->getAccess();
return Spec;
}
} // namespace
SourceLocation insertionPoint(const DeclContext &DC,
llvm::ArrayRef<Anchor> Anchors) {
dlog("Looking for insertion point in {0}", DC.getDeclKindName());
for (const auto &A : Anchors) {
dlog(" anchor ({0})", A.Direction == Anchor::Above ? "above" : "below");
if (auto D = insertionDecl(DC, A)) {
dlog(" anchor matched before {0}", *D);
return *D ? beginLoc(**D) : endLoc(DC);
}
}
dlog("no anchor matched");
return SourceLocation();
}
llvm::Expected<tooling::Replacement>
insertDecl(llvm::StringRef Code, const DeclContext &DC,
llvm::ArrayRef<Anchor> Anchors) {
auto Loc = insertionPoint(DC, Anchors);
// Fallback: insert at the end.
if (Loc.isInvalid())
Loc = endLoc(DC);
const auto &SM = DC.getParentASTContext().getSourceManager();
if (!SM.isWrittenInSameFile(Loc, cast<Decl>(DC).getLocation()))
return error("{0} body in wrong file: {1}", DC.getDeclKindName(),
Loc.printToString(SM));
return tooling::Replacement(SM, Loc, 0, Code);
}
SourceLocation insertionPoint(const CXXRecordDecl &InClass,
std::vector<Anchor> Anchors,
AccessSpecifier Protection) {
for (auto &A : Anchors)
A.Match = [Inner(std::move(A.Match)), Protection](const Decl *D) {
return D->getAccess() == Protection && Inner(D);
};
return insertionPoint(InClass, Anchors);
}
llvm::Expected<tooling::Replacement> insertDecl(llvm::StringRef Code,
const CXXRecordDecl &InClass,
std::vector<Anchor> Anchors,
AccessSpecifier Protection) {
// Fallback: insert at the bottom of the relevant access section.
Anchors.push_back({any, Anchor::Below});
auto Loc = insertionPoint(InClass, std::move(Anchors), Protection);
std::string CodeBuffer;
auto &SM = InClass.getASTContext().getSourceManager();
// Fallback: insert at the end of the class. Check if protection matches!
if (Loc.isInvalid()) {
Loc = InClass.getBraceRange().getEnd();
if (Protection != getAccessAtEnd(InClass)) {
CodeBuffer = (getAccessSpelling(Protection) + ":\n" + Code).str();
Code = CodeBuffer;
}
}
if (!SM.isWrittenInSameFile(Loc, InClass.getLocation()))
return error("Class body in wrong file: {0}", Loc.printToString(SM));
return tooling::Replacement(SM, Loc, 0, Code);
}
} // namespace clangd
} // namespace clang