12 #include "clang/AST/ASTTypeTraits.h" 13 #include "clang/AST/DeclCXX.h" 14 #include "clang/AST/Expr.h" 15 #include "clang/AST/ExprCXX.h" 16 #include "clang/AST/PrettyPrinter.h" 17 #include "clang/AST/RecursiveASTVisitor.h" 18 #include "clang/AST/TypeLoc.h" 19 #include "clang/Basic/OperatorKinds.h" 20 #include "clang/Basic/SourceLocation.h" 21 #include "clang/Basic/SourceManager.h" 22 #include "clang/Basic/TokenKinds.h" 23 #include "clang/Lex/Lexer.h" 24 #include "clang/Tooling/Syntax/Tokens.h" 25 #include "llvm/ADT/STLExtras.h" 26 #include "llvm/Support/Casting.h" 27 #include "llvm/Support/raw_ostream.h" 34 using Node = SelectionTree::Node;
35 using ast_type_traits::DynTypedNode;
58 IntervalSet(llvm::ArrayRef<T>
Range) { UnclaimedRanges.insert(Range); }
63 llvm::SmallVector<llvm::ArrayRef<T>, 4> erase(llvm::ArrayRef<T> Claim) {
64 llvm::SmallVector<llvm::ArrayRef<T>, 4> Out;
73 auto Overlap = std::make_pair(
74 UnclaimedRanges.lower_bound({Claim.begin(), Claim.begin()}),
75 UnclaimedRanges.lower_bound({Claim.end(), Claim.end()}));
77 if (Overlap.first != UnclaimedRanges.begin()) {
80 if (Overlap.first->end() <= Claim.begin())
83 if (Overlap.first == Overlap.second)
87 auto OutFirst = Out.insert(Out.end(), Overlap.first, Overlap.second);
91 llvm::ArrayRef<T> RemainingHead, RemainingTail;
92 if (Claim.begin() > OutFirst->begin()) {
93 RemainingHead = {OutFirst->begin(), Claim.begin()};
94 *OutFirst = {Claim.begin(), OutFirst->end()};
96 if (Claim.end() < Out.back().end()) {
97 RemainingTail = {Claim.end(), Out.back().end()};
98 Out.back() = {Out.back().begin(), Claim.end()};
102 UnclaimedRanges.erase(Overlap.first, Overlap.second);
104 if (!RemainingHead.empty())
105 UnclaimedRanges.insert(RemainingHead);
106 if (!RemainingTail.empty())
107 UnclaimedRanges.insert(RemainingTail);
113 using TokenRange = llvm::ArrayRef<T>;
115 bool operator()(llvm::ArrayRef<T> L, llvm::ArrayRef<T> R)
const {
116 return L.begin() < R.begin();
121 std::set<llvm::ArrayRef<T>, RangeLess>
138 if (Result == NoTokens)
140 else if (Result != New)
157 class SelectionTester {
160 SelectionTester(
const syntax::TokenBuffer &Buf, FileID SelFile,
161 unsigned SelBegin,
unsigned SelEnd,
const SourceManager &SM)
162 : SelFile(SelFile), SM(SM) {
164 auto AllSpelledTokens = Buf.spelledTokens(SelFile);
165 const syntax::Token *SelFirst =
166 llvm::partition_point(AllSpelledTokens, [&](
const syntax::Token &Tok) {
167 return SM.getFileOffset(Tok.endLocation()) <= SelBegin;
169 const syntax::Token *SelLimit = std::partition_point(
170 SelFirst, AllSpelledTokens.end(), [&](
const syntax::Token &Tok) {
171 return SM.getFileOffset(Tok.location()) < SelEnd;
174 for (
const syntax::Token *T = SelFirst; T < SelLimit; ++T) {
177 if (T->kind() == tok::comment || T->kind() == tok::semi)
179 SpelledTokens.emplace_back();
180 Tok &S = SpelledTokens.back();
181 S.Offset = SM.getFileOffset(T->location());
182 if (S.Offset >= SelBegin && S.Offset + T->length() <= SelEnd)
192 test(llvm::ArrayRef<syntax::Token> ExpandedTokens)
const {
193 if (SpelledTokens.empty())
196 while (!ExpandedTokens.empty()) {
198 FileID FID = SM.getFileID(ExpandedTokens.front().location());
199 auto Batch = ExpandedTokens.take_while([&](
const syntax::Token &T) {
200 return SM.getFileID(T.location()) == FID;
202 assert(!Batch.empty());
203 ExpandedTokens = ExpandedTokens.drop_front(Batch.size());
205 update(Result, testChunk(FID, Batch));
213 bool mayHit(SourceRange R)
const {
214 if (SpelledTokens.empty())
216 auto B = SM.getDecomposedLoc(R.getBegin());
217 auto E = SM.getDecomposedLoc(R.getEnd());
218 if (B.first == SelFile &&
E.first == SelFile)
219 if (
E.second < SpelledTokens.front().Offset ||
220 B.second > SpelledTokens.back().Offset)
228 testChunk(FileID FID, llvm::ArrayRef<syntax::Token> Batch)
const {
229 assert(!Batch.empty());
230 SourceLocation StartLoc = Batch.front().location();
239 if (FID == SelFile) {
240 return testTokenRange(SM.getFileOffset(Batch.front().location()),
241 SM.getFileOffset(Batch.back().location()));
246 if (StartLoc.isFileID()) {
247 for (SourceLocation
Loc = Batch.front().location();
Loc.isValid();
248 Loc = SM.getIncludeLoc(SM.getFileID(
Loc))) {
249 if (SM.getFileID(
Loc) == SelFile)
251 return testToken(SM.getFileOffset(
Loc));
256 assert(StartLoc.isMacroID());
258 SourceLocation ArgStart = SM.getTopMacroCallerLoc(StartLoc);
259 if (SM.getFileID(ArgStart) == SelFile) {
260 SourceLocation ArgEnd = SM.getTopMacroCallerLoc(Batch.back().location());
261 return testTokenRange(SM.getFileOffset(ArgStart),
262 SM.getFileOffset(ArgEnd));
267 auto Expansion = SM.getDecomposedExpansionLoc(StartLoc);
268 if (Expansion.first == SelFile)
270 return testToken(Expansion.second);
277 assert(Begin <= End);
279 if (End < SpelledTokens.front().Offset ||
280 Begin > SpelledTokens.back().Offset)
284 auto B = llvm::partition_point(
285 SpelledTokens, [&](
const Tok &T) {
return T.Offset < Begin; });
286 auto E = std::partition_point(
287 B, SpelledTokens.end(), [&](
const Tok &T) {
return T.Offset <= End; });
290 bool ExtendsOutsideSelection = Begin < SpelledTokens.front().Offset ||
291 End > SpelledTokens.back().Offset;
294 for (
auto It = B; It !=
E; ++It)
295 update(Result, It->Selected);
302 if (Offset < SpelledTokens.front().Offset ||
303 Offset > SpelledTokens.back().Offset)
306 auto It = llvm::partition_point(
307 SpelledTokens, [&](
const Tok &T) {
return T.Offset <
Offset; });
308 if (It != SpelledTokens.end() && It->Offset ==
Offset)
317 std::vector<Tok> SpelledTokens;
319 const SourceManager &SM;
323 void printNodeKind(llvm::raw_ostream &OS,
const DynTypedNode &N) {
324 if (
const TypeLoc *TL = N.get<TypeLoc>()) {
327 if (TL->getTypeLocClass() == TypeLoc::Qualified)
328 OS <<
"QualifiedTypeLoc";
330 OS << TL->getType()->getTypeClassName() <<
"TypeLoc";
332 OS << N.getNodeKind().asStringRef();
337 std::string printNodeToString(
const DynTypedNode &N,
const PrintingPolicy &
PP) {
339 llvm::raw_string_ostream OS(S);
340 printNodeKind(OS, N);
342 return std::move(OS.str());
346 bool isImplicit(
const Stmt* S) {
350 if (
auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(S))
351 S = ICE->getSubExprAsWritten();
354 if (
auto *CTI = llvm::dyn_cast<CXXThisExpr>(S))
355 if (CTI->isImplicit())
358 if (
auto *DRE = llvm::dyn_cast<DeclRefExpr>(S)) {
359 if (
auto *FD = llvm::dyn_cast<FunctionDecl>(DRE->getDecl())) {
360 switch (FD->getOverloadedOperator()) {
380 class SelectionVisitor :
public RecursiveASTVisitor<SelectionVisitor> {
384 static std::deque<Node> collect(ASTContext &
AST,
385 const syntax::TokenBuffer &Tokens,
386 const PrintingPolicy &PP,
unsigned Begin,
387 unsigned End, FileID
File) {
388 SelectionVisitor V(AST, Tokens, PP, Begin, End, File);
390 assert(V.Stack.size() == 1 &&
"Unpaired push/pop?");
391 assert(V.Stack.top() == &V.Nodes.front());
392 return std::move(V.Nodes);
406 bool TraverseDecl(
Decl *
X) {
407 if (X && isa<TranslationUnitDecl>(X))
408 return Base::TraverseDecl(X);
410 if (X && X->isImplicit())
412 return traverseNode(X, [&] {
return Base::TraverseDecl(X); });
414 bool TraverseTypeLoc(TypeLoc X) {
415 return traverseNode(&X, [&] {
return Base::TraverseTypeLoc(X); });
417 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc X) {
419 &X, [&] {
return Base::TraverseNestedNameSpecifierLoc(X); });
421 bool TraverseConstructorInitializer(CXXCtorInitializer *X) {
423 X, [&] {
return Base::TraverseConstructorInitializer(X); });
426 bool dataTraverseStmtPre(Stmt *X) {
427 if (!X || isImplicit(X))
429 auto N = DynTypedNode::create(*X);
430 if (canSafelySkipNode(N))
433 if (shouldSkipChildren(X)) {
439 bool dataTraverseStmtPost(Stmt *X) {
448 bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX) {
449 return traverseNode<TypeLoc>(
450 &QX, [&] {
return TraverseTypeLoc(QX.getUnqualifiedLoc()); });
453 bool TraverseNestedNameSpecifier(NestedNameSpecifier *) {
return true; }
454 bool TraverseType(QualType) {
return true; }
459 bool TraverseCXXForRangeStmt(CXXForRangeStmt *S) {
460 return traverseNode(S, [&] {
461 return TraverseStmt(S->getInit()) && TraverseDecl(S->getLoopVariable()) &&
462 TraverseStmt(S->getRangeInit()) && TraverseStmt(S->getBody());
467 using Base = RecursiveASTVisitor<SelectionVisitor>;
469 SelectionVisitor(ASTContext &AST,
const syntax::TokenBuffer &Tokens,
470 const PrintingPolicy &PP,
unsigned SelBegin,
unsigned SelEnd,
472 : SM(AST.getSourceManager()), LangOpts(AST.getLangOpts()),
476 TokenBuf(Tokens), SelChecker(Tokens, SelFile, SelBegin, SelEnd, SM),
477 UnclaimedExpandedTokens(Tokens.expandedTokens()) {
479 Nodes.emplace_back();
480 Nodes.back().ASTNode = DynTypedNode::create(*AST.getTranslationUnitDecl());
481 Nodes.back().Parent =
nullptr;
483 Stack.push(&Nodes.back());
488 template <
typename T,
typename Func>
489 bool traverseNode(T *Node,
const Func &Body) {
492 auto N = DynTypedNode::create(*Node);
493 if (canSafelySkipNode(N))
495 push(DynTypedNode::create(*Node));
528 bool canSafelySkipNode(
const DynTypedNode &N) {
529 SourceRange S = N.getSourceRange();
530 if (
auto *TL = N.get<TypeLoc>()) {
540 if (
auto DT = TL->getAs<DecltypeTypeLoc>())
541 S.setEnd(DT.getUnderlyingExpr()->getEndLoc());
543 if (!SelChecker.mayHit(S)) {
544 dlog(
"{1}skip: {0}", printNodeToString(N, PrintPolicy), indent());
545 dlog(
"{1}skipped range = {0}", S.printToString(SM), indent(1));
553 bool shouldSkipChildren(
const Stmt *X)
const {
557 return llvm::isa<UserDefinedLiteral>(
X);
562 void push(DynTypedNode Node) {
563 SourceRange Early = earlySourceRange(Node);
564 dlog(
"{1}push: {0}", printNodeToString(Node, PrintPolicy), indent());
565 Nodes.emplace_back();
566 Nodes.back().ASTNode = std::move(Node);
567 Nodes.back().Parent = Stack.top();
568 Nodes.back().Selected = NoTokens;
569 Stack.push(&Nodes.back());
570 claimRange(Early, Nodes.back().Selected);
576 Node &N = *Stack.top();
577 dlog(
"{1}pop: {0}", printNodeToString(N.ASTNode, PrintPolicy), indent(-1));
578 claimRange(N.ASTNode.getSourceRange(), N.Selected);
579 if (N.Selected == NoTokens)
581 if (N.Selected || !N.Children.empty()) {
583 N.Parent->Children.push_back(&N);
586 assert(&N == &Nodes.back());
595 SourceRange earlySourceRange(
const DynTypedNode &N) {
596 if (
const Decl *D = N.get<
Decl>()) {
598 if (
auto *FD = llvm::dyn_cast<FunctionDecl>(D))
599 return FD->getNameInfo().getSourceRange();
601 else if (
auto *VD = llvm::dyn_cast<VarDecl>(D))
602 return VD->getLocation();
603 }
else if (
const auto* CCI = N.get<CXXCtorInitializer>()) {
605 return CCI->getMemberLocation();
607 return SourceRange();
615 for (
const auto &ClaimedRange :
616 UnclaimedExpandedTokens.erase(TokenBuf.expandedTokens(S)))
617 update(Result, SelChecker.test(ClaimedRange));
619 if (Result && Result != NoTokens)
620 dlog(
"{1}hit selection: {0}", S.printToString(SM), indent());
623 std::string indent(
int Offset = 0) {
625 int Amount = int(Stack.size()) + Offset;
627 return std::string(Amount,
' ');
631 const LangOptions &LangOpts;
633 const PrintingPolicy &PrintPolicy;
635 const syntax::TokenBuffer &TokenBuf;
636 std::stack<Node *> Stack;
637 SelectionTester SelChecker;
638 IntervalSet<syntax::Token> UnclaimedExpandedTokens;
639 std::deque<Node> Nodes;
644 void SelectionTree::print(llvm::raw_ostream &OS,
const SelectionTree::Node &N,
651 printNodeKind(OS, N.ASTNode);
653 N.ASTNode.print(OS, PrintPolicy);
655 for (
const Node *Child : N.Children)
656 print(OS, *Child, Indent + 2);
661 llvm::raw_string_ostream OS(S);
662 printNodeKind(OS, ASTNode);
663 return std::move(OS.str());
667 static std::pair<unsigned, unsigned>
pointBounds(
unsigned Offset, FileID FID,
669 StringRef Buf = AST.getSourceManager().getBufferData(FID);
675 if (Offset == Buf.size())
676 return {Offset - 1, Offset};
680 auto IsIgnoredChar = [](
char C) {
return isWhitespace(C) || C ==
';'; };
681 if (IsIgnoredChar(Buf[Offset]) && !IsIgnoredChar(Buf[Offset - 1]))
682 return {Offset - 1, Offset};
683 return {
Offset, Offset + 1};
687 unsigned Begin,
unsigned End)
688 : PrintPolicy(AST.getLangOpts()) {
691 const SourceManager &SM = AST.getSourceManager();
692 FileID FID = SM.getMainFileID();
694 std::tie(Begin, End) =
pointBounds(Begin, FID, AST);
695 PrintPolicy.TerseOutput =
true;
696 PrintPolicy.IncludeNewlines =
false;
698 dlog(
"Computing selection for {0}",
699 SourceRange(SM.getComposedLoc(FID, Begin), SM.getComposedLoc(FID, End))
701 Nodes = SelectionVisitor::collect(AST, Tokens, PrintPolicy, Begin, End, FID);
702 Root = Nodes.empty() ? nullptr : &Nodes.front();
703 dlog(
"Built selection tree\n{0}", *
this);
711 const Node *Ancestor = Root;
713 Ancestor = Ancestor->
Children.front();
717 return Ancestor != Root ? Ancestor :
nullptr;
721 for (
const Node* CurrentNode =
this; CurrentNode !=
nullptr;
722 CurrentNode = CurrentNode->
Parent) {
723 if (
const Decl* Current = CurrentNode->ASTNode.get<
Decl>()) {
724 if (CurrentNode !=
this)
725 if (
auto *DC = dyn_cast<DeclContext>(Current))
727 return *Current->getDeclContext();
730 llvm_unreachable(
"A tree must always be rooted at TranslationUnitDecl.");
735 Children.front()->ASTNode.getSourceRange() == ASTNode.getSourceRange())
736 return Children.front()->ignoreImplicit();
SourceLocation Loc
'#' location in the include directive
const FunctionDecl * Decl
llvm::SmallVector< const Node *, 8 > Children
const Node & ignoreImplicit() const
SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Offset)
const Node & outerImplicit() const
static std::pair< unsigned, unsigned > pointBounds(unsigned Offset, FileID FID, ASTContext &AST)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
CharSourceRange Range
SourceRange for the file name.
ast_type_traits::DynTypedNode ASTNode
const Node * commonAncestor() const
std::unique_ptr< GlobalCompilationDatabase > Base
SelectionTree::Selection Selected
const DeclContext & getDeclContext() const