13#include "clang/AST/ASTConcept.h"
14#include "clang/AST/ASTTypeTraits.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/DeclCXX.h"
17#include "clang/AST/Expr.h"
18#include "clang/AST/ExprCXX.h"
19#include "clang/AST/PrettyPrinter.h"
20#include "clang/AST/RecursiveASTVisitor.h"
21#include "clang/AST/TypeLoc.h"
22#include "clang/Basic/OperatorKinds.h"
23#include "clang/Basic/SourceLocation.h"
24#include "clang/Basic/SourceManager.h"
25#include "clang/Basic/TokenKinds.h"
26#include "clang/Lex/Lexer.h"
27#include "clang/Tooling/Syntax/Tokens.h"
28#include "llvm/ADT/BitVector.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/StringExtras.h"
31#include "llvm/Support/Casting.h"
32#include "llvm/Support/raw_ostream.h"
44void recordMetrics(
const SelectionTree &S,
const LangOptions &Lang) {
47 const char *LanguageLabel = Lang.CPlusPlus ?
"C++" : Lang.ObjC ?
"ObjC" :
"C";
52 const auto *Common = S.commonAncestor();
53 for (
const auto *N = Common; N; N = N->Parent) {
54 if (
const auto *RE = N->ASTNode.get<RecoveryExpr>()) {
55 SelectionUsedRecovery.record(1, LanguageLabel);
56 RecoveryType.record(RE->isTypeDependent() ? 0 : 1, LanguageLabel);
61 SelectionUsedRecovery.record(0, LanguageLabel);
65SourceRange getSourceRange(
const DynTypedNode &N,
66 bool IncludeQualifier =
false) {
78 if (
const auto *ME = N.get<MemberExpr>()) {
79 if (!ME->getMemberDecl()->getDeclName())
81 ? getSourceRange(DynTypedNode::create(*ME->getBase()))
84 return N.getSourceRange(IncludeQualifier);
105template <
typename T>
class IntervalSet {
107 IntervalSet(llvm::ArrayRef<T> Range) { UnclaimedRanges.insert(Range); }
112 llvm::SmallVector<llvm::ArrayRef<T>> erase(llvm::ArrayRef<T> Claim) {
113 llvm::SmallVector<llvm::ArrayRef<T>> Out;
122 auto Overlap = std::make_pair(
123 UnclaimedRanges.lower_bound({Claim.begin(), Claim.begin()}),
124 UnclaimedRanges.lower_bound({Claim.end(), Claim.end()}));
126 if (Overlap.first != UnclaimedRanges.begin()) {
129 if (Overlap.first->end() <= Claim.begin())
132 if (Overlap.first == Overlap.second)
136 auto OutFirst = Out.insert(Out.end(), Overlap.first, Overlap.second);
140 llvm::ArrayRef<T> RemainingHead, RemainingTail;
141 if (Claim.begin() > OutFirst->begin()) {
142 RemainingHead = {OutFirst->begin(), Claim.begin()};
143 *OutFirst = {Claim.begin(), OutFirst->end()};
145 if (Claim.end() < Out.back().end()) {
146 RemainingTail = {Claim.end(), Out.back().end()};
147 Out.back() = {Out.back().begin(), Claim.end()};
151 UnclaimedRanges.erase(Overlap.first, Overlap.second);
153 if (!RemainingHead.empty())
154 UnclaimedRanges.insert(RemainingHead);
155 if (!RemainingTail.empty())
156 UnclaimedRanges.insert(RemainingTail);
162 using TokenRange = llvm::ArrayRef<T>;
164 bool operator()(llvm::ArrayRef<T> L, llvm::ArrayRef<T> R)
const {
165 return L.begin() < R.begin();
170 std::set<llvm::ArrayRef<T>, RangeLess> UnclaimedRanges;
186 if (Result == NoTokens)
188 else if (Result != New)
195bool shouldIgnore(
const syntax::Token &Tok) {
196 switch (Tok.kind()) {
203 case tok::kw_volatile:
204 case tok::kw_restrict:
213bool isFirstExpansion(FileID Target, SourceLocation SpellingLoc,
214 const SourceManager &SM) {
215 SourceLocation Prev = SpellingLoc;
219 SourceLocation Next = SM.getMacroArgExpandedLocation(Prev);
222 if (SM.getFileID(Next) == Target)
227 if (SM.getFileID(Next) == SM.getFileID(Prev))
248class SelectionTester {
251 SelectionTester(
const syntax::TokenBuffer &Buf, FileID SelFile,
252 unsigned SelBegin,
unsigned SelEnd,
const SourceManager &SM)
253 : SelFile(SelFile), SelFileBounds(SM.getLocForStartOfFile(SelFile),
254 SM.getLocForEndOfFile(SelFile)),
257 auto AllSpelledTokens = Buf.spelledTokens(SelFile);
258 const syntax::Token *SelFirst =
259 llvm::partition_point(AllSpelledTokens, [&](
const syntax::Token &Tok) {
260 return SM.getFileOffset(Tok.endLocation()) <= SelBegin;
262 const syntax::Token *SelLimit = std::partition_point(
263 SelFirst, AllSpelledTokens.end(), [&](
const syntax::Token &Tok) {
264 return SM.getFileOffset(Tok.location()) < SelEnd;
266 auto Sel = llvm::ArrayRef(SelFirst, SelLimit);
268 llvm::BitVector PPIgnored(Sel.size(),
false);
269 for (
const syntax::TokenBuffer::Expansion &
X :
270 Buf.expansionsOverlapping(Sel)) {
271 if (
X.Expanded.empty()) {
272 for (
const syntax::Token &Tok :
X.Spelled) {
273 if (&Tok >= SelFirst && &Tok < SelLimit)
274 PPIgnored[&Tok - SelFirst] =
true;
279 for (
unsigned I = 0; I < Sel.size(); ++I) {
280 if (shouldIgnore(Sel[I]) || PPIgnored[I])
282 SelectedSpelled.emplace_back();
283 Tok &S = SelectedSpelled.back();
284 S.Offset = SM.getFileOffset(Sel[I].location());
285 if (S.Offset >= SelBegin && S.Offset + Sel[I].length() <= SelEnd)
290 MaybeSelectedExpanded = computeMaybeSelectedExpandedTokens(Buf);
296 test(llvm::ArrayRef<syntax::Token> ExpandedTokens)
const {
297 if (ExpandedTokens.empty())
299 if (SelectedSpelled.empty())
307 if (MaybeSelectedExpanded.empty() ||
308 &ExpandedTokens.front() > &MaybeSelectedExpanded.back() ||
309 &ExpandedTokens.back() < &MaybeSelectedExpanded.front()) {
318 if (ExpandedTokens.back().kind() == tok::eof)
319 ExpandedTokens = ExpandedTokens.drop_back();
322 while (!ExpandedTokens.empty()) {
324 SourceLocation Start = ExpandedTokens.front().location();
325 FileID FID = SM.getFileID(Start);
327 SourceLocation Limit = SM.getComposedLoc(FID, SM.getFileIDSize(FID));
328 auto Batch = ExpandedTokens.take_while([&](
const syntax::Token &T) {
329 return T.location() >= Start &&
T.location() < Limit;
331 assert(!Batch.empty());
332 ExpandedTokens = ExpandedTokens.drop_front(Batch.size());
334 update(Result, testChunk(FID, Batch));
342 bool mayHit(SourceRange R)
const {
343 if (SelectedSpelled.empty() || MaybeSelectedExpanded.empty())
349 if (
auto B = offsetInSelFile(getExpansionStart(R.getBegin())))
350 if (*B > SelectedSpelled.back().Offset)
353 SourceLocation EndLoc = R.getEnd();
354 while (EndLoc.isMacroID())
355 EndLoc = SM.getImmediateExpansionRange(EndLoc).getEnd();
358 if (
auto E = offsetInSelFile(EndLoc))
359 if (*E < SelectedSpelled.front().Offset)
368 llvm::ArrayRef<syntax::Token>
369 computeMaybeSelectedExpandedTokens(
const syntax::TokenBuffer &Toks) {
370 if (SelectedSpelled.empty())
373 auto LastAffectedToken = [&](SourceLocation Loc) {
374 auto Offset = offsetInSelFile(Loc);
375 while (Loc.isValid() && !Offset) {
376 Loc = Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).getEnd()
377 : SM.getIncludeLoc(SM.getFileID(Loc));
378 Offset = offsetInSelFile(Loc);
382 auto FirstAffectedToken = [&](SourceLocation Loc) {
383 auto Offset = offsetInSelFile(Loc);
384 while (Loc.isValid() && !Offset) {
385 Loc = Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).getBegin()
386 : SM.getIncludeLoc(SM.getFileID(Loc));
387 Offset = offsetInSelFile(Loc);
392 const syntax::Token *Start = llvm::partition_point(
393 Toks.expandedTokens(),
394 [&, First = SelectedSpelled.front().Offset](
const syntax::Token &Tok) {
395 if (Tok.kind() == tok::eof)
398 if (auto Offset = LastAffectedToken(Tok.location()))
399 return *Offset < First;
405 bool EndInvalid =
false;
406 const syntax::Token *End = std::partition_point(
407 Start, Toks.expandedTokens().end(),
408 [&, Last = SelectedSpelled.back().Offset](
const syntax::Token &Tok) {
409 if (Tok.kind() == tok::eof)
412 if (auto Offset = FirstAffectedToken(Tok.location()))
413 return *Offset <= Last;
416 assert(false &&
"Expanded token could not be resolved to main file!");
421 End = Toks.expandedTokens().end();
423 return llvm::ArrayRef(Start, End);
428 testChunk(FileID FID, llvm::ArrayRef<syntax::Token> Batch)
const {
429 assert(!Batch.empty());
430 SourceLocation StartLoc = Batch.front().location();
439 if (FID == SelFile) {
440 return testTokenRange(*offsetInSelFile(Batch.front().location()),
441 *offsetInSelFile(Batch.back().location()));
446 if (StartLoc.isFileID()) {
447 for (SourceLocation Loc = Batch.front().location(); Loc.isValid();
448 Loc = SM.getIncludeLoc(SM.getFileID(Loc))) {
449 if (
auto Offset = offsetInSelFile(Loc))
451 return testToken(*Offset);
456 assert(StartLoc.isMacroID());
458 SourceLocation ArgStart = SM.getTopMacroCallerLoc(StartLoc);
459 if (
auto ArgOffset = offsetInSelFile(ArgStart)) {
460 if (isFirstExpansion(FID, ArgStart, SM)) {
461 SourceLocation ArgEnd =
462 SM.getTopMacroCallerLoc(Batch.back().location());
463 return testTokenRange(*ArgOffset, *offsetInSelFile(ArgEnd));
471 if (
auto ExpansionOffset = offsetInSelFile(getExpansionStart(StartLoc)))
473 return testToken(*ExpansionOffset);
479 assert(Begin <= End);
481 if (End < SelectedSpelled.front().Offset ||
482 Begin > SelectedSpelled.back().Offset)
486 auto B = llvm::partition_point(
487 SelectedSpelled, [&](
const Tok &T) {
return T.Offset < Begin; });
488 auto E = std::partition_point(B, SelectedSpelled.end(), [&](
const Tok &T) {
489 return T.Offset <= End;
493 bool ExtendsOutsideSelection = Begin < SelectedSpelled.front().Offset ||
494 End > SelectedSpelled.back().Offset;
497 for (
auto It = B; It != E; ++It)
498 update(Result, It->Selected);
505 if (Offset < SelectedSpelled.front().Offset ||
506 Offset > SelectedSpelled.back().Offset)
509 auto It = llvm::partition_point(
510 SelectedSpelled, [&](
const Tok &T) {
return T.Offset < Offset; });
511 if (It != SelectedSpelled.end() && It->Offset == Offset)
517 std::optional<unsigned> offsetInSelFile(SourceLocation Loc)
const {
521 if (Loc < SelFileBounds.getBegin() || Loc >= SelFileBounds.getEnd())
524 return Loc.getRawEncoding() - SelFileBounds.getBegin().getRawEncoding();
527 SourceLocation getExpansionStart(SourceLocation Loc)
const {
528 while (Loc.isMacroID())
529 Loc = SM.getImmediateExpansionRange(Loc).getBegin();
537 std::vector<Tok> SelectedSpelled;
538 llvm::ArrayRef<syntax::Token> MaybeSelectedExpanded;
540 SourceRange SelFileBounds;
541 const SourceManager &SM;
545void printNodeKind(llvm::raw_ostream &OS,
const DynTypedNode &N) {
546 if (
const TypeLoc *TL = N.get<TypeLoc>()) {
549 if (TL->getTypeLocClass() == TypeLoc::Qualified)
550 OS <<
"QualifiedTypeLoc";
552 OS << TL->getType()->getTypeClassName() <<
"TypeLoc";
554 OS << N.getNodeKind().asStringRef();
559std::string printNodeToString(
const DynTypedNode &N,
const PrintingPolicy &PP) {
561 llvm::raw_string_ostream OS(S);
562 printNodeKind(OS, N);
563 return std::move(OS.str());
567bool isImplicit(
const Stmt *S) {
571 if (
auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(S))
572 S = ICE->getSubExprAsWritten();
575 if (
auto *CTI = llvm::dyn_cast<CXXThisExpr>(S))
576 if (CTI->isImplicit())
579 if (
auto *ME = llvm::dyn_cast<MemberExpr>(S)) {
580 if (
auto *FD = llvm::dyn_cast<FieldDecl>(ME->getMemberDecl()))
581 if (FD->isAnonymousStructOrUnion())
585 return isImplicit(ME->getBase());
588 if (
auto *DRE = llvm::dyn_cast<DeclRefExpr>(S)) {
589 if (
auto *FD = llvm::dyn_cast<FunctionDecl>(DRE->getDecl())) {
590 switch (FD->getOverloadedOperator()) {
610class SelectionVisitor :
public RecursiveASTVisitor<SelectionVisitor> {
614 static std::deque<Node> collect(ASTContext &
AST,
615 const syntax::TokenBuffer &Tokens,
616 const PrintingPolicy &PP,
unsigned Begin,
617 unsigned End, FileID
File) {
618 SelectionVisitor V(
AST, Tokens, PP, Begin, End,
File);
620 assert(V.Stack.size() == 1 &&
"Unpaired push/pop?");
621 assert(V.Stack.top() == &V.Nodes.front());
622 return std::move(V.Nodes);
635 bool TraverseDecl(Decl *
X) {
636 if (llvm::isa_and_nonnull<TranslationUnitDecl>(
X))
637 return Base::TraverseDecl(
X);
639 if (
X &&
X->isImplicit()) {
643 return Base::TraverseDecl(
X);
645 return traverseNode(
X, [&] {
return Base::TraverseDecl(
X); });
647 bool TraverseTypeLoc(TypeLoc
X,
bool TraverseQualifier =
true) {
649 &
X, [&] {
return Base::TraverseTypeLoc(
X, TraverseQualifier); });
651 bool TraverseTemplateArgumentLoc(
const TemplateArgumentLoc &
X) {
652 return traverseNode(&
X,
653 [&] {
return Base::TraverseTemplateArgumentLoc(
X); });
655 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc
X) {
657 &
X, [&] {
return Base::TraverseNestedNameSpecifierLoc(
X); });
659 bool TraverseConstructorInitializer(CXXCtorInitializer *
X) {
661 X, [&] {
return Base::TraverseConstructorInitializer(
X); });
663 bool TraverseCXXBaseSpecifier(
const CXXBaseSpecifier &
X) {
664 return traverseNode(&
X, [&] {
return Base::TraverseCXXBaseSpecifier(
X); });
666 bool TraverseAttr(Attr *
X) {
667 return traverseNode(
X, [&] {
return Base::TraverseAttr(
X); });
669 bool TraverseConceptReference(ConceptReference *
X) {
670 return traverseNode(
X, [&] {
return Base::TraverseConceptReference(
X); });
673 bool dataTraverseStmtPre(Stmt *
X) {
674 if (!
X || isImplicit(
X))
676 auto N = DynTypedNode::create(*
X);
677 if (canSafelySkipNode(N))
680 if (shouldSkipChildren(
X)) {
686 bool dataTraverseStmtPost(Stmt *
X) {
695 bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX,
696 bool TraverseQualifier =
true) {
697 return traverseNode<TypeLoc>(
698 &QX, [&] {
return TraverseTypeLoc(QX.getUnqualifiedLoc()); });
700 bool TraverseObjCProtocolLoc(ObjCProtocolLoc PL) {
701 return traverseNode(&PL, [&] {
return Base::TraverseObjCProtocolLoc(PL); });
704 bool TraverseNestedNameSpecifier(NestedNameSpecifier) {
return true; }
705 bool TraverseType(QualType) {
return true; }
710 bool TraverseCXXForRangeStmt(CXXForRangeStmt *S) {
711 return traverseNode(S, [&] {
712 return TraverseStmt(S->getInit()) && TraverseDecl(S->getLoopVariable()) &&
713 TraverseStmt(S->getRangeInit()) && TraverseStmt(S->getBody());
717 bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) {
718 return traverseNode(E, [&] {
return TraverseStmt(E->getSourceExpr()); });
721 bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
722 return traverseNode(E, [&] {
return TraverseStmt(E->getSyntacticForm()); });
724 bool TraverseTypeConstraint(
const TypeConstraint *C) {
725 if (
auto *E =
C->getImmediatelyDeclaredConstraint()) {
729 return TraverseStmt(E);
731 return Base::TraverseTypeConstraint(C);
735 using RecursiveASTVisitor::getStmtChildren;
738 Stmt::child_range getStmtChildren(PredefinedExpr *) {
739 return {StmtIterator{}, StmtIterator{}};
743 using Base = RecursiveASTVisitor<SelectionVisitor>;
745 SelectionVisitor(ASTContext &
AST,
const syntax::TokenBuffer &Tokens,
746 const PrintingPolicy &PP,
unsigned SelBegin,
unsigned SelEnd,
748 : SM(
AST.getSourceManager()), LangOpts(
AST.getLangOpts()),
752 TokenBuf(Tokens), SelChecker(Tokens, SelFile, SelBegin, SelEnd, SM),
753 UnclaimedExpandedTokens(Tokens.expandedTokens()) {
755 Nodes.emplace_back();
756 Nodes.back().ASTNode = DynTypedNode::create(*
AST.getTranslationUnitDecl());
757 Nodes.back().Parent =
nullptr;
759 Stack.push(&Nodes.back());
764 template <
typename T,
typename Func>
765 bool traverseNode(T *Node,
const Func &Body) {
768 auto N = DynTypedNode::create(*Node);
769 if (canSafelySkipNode(N))
771 push(DynTypedNode::create(*Node));
803 bool canSafelySkipNode(
const DynTypedNode &N) {
804 SourceRange S = getSourceRange(N,
true);
805 if (
auto *TL = N.get<TypeLoc>()) {
812 if (
auto AT = TL->getAs<AttributedTypeLoc>())
813 S = AT.getModifiedLoc().getSourceRange();
818 [](
const Attr *A) {
return !
A->isImplicit(); }))
820 if (!SelChecker.mayHit(S)) {
821 dlog(
"{2}skip: {0} {1}", printNodeToString(N, PrintPolicy),
822 S.printToString(SM), indent());
830 bool shouldSkipChildren(
const Stmt *
X)
const {
834 return llvm::isa<UserDefinedLiteral>(
X);
839 void push(DynTypedNode Node) {
840 SourceRange Early = earlySourceRange(Node);
841 dlog(
"{2}push: {0} {1}", printNodeToString(Node, PrintPolicy),
842 Node.getSourceRange().printToString(SM), indent());
843 Nodes.emplace_back();
844 Nodes.back().ASTNode = std::move(Node);
845 Nodes.back().Parent = Stack.top();
846 Nodes.back().Selected = NoTokens;
847 Stack.push(&Nodes.back());
848 claimRange(Early, Nodes.back().Selected);
854 Node &N = *Stack.top();
855 dlog(
"{1}pop: {0}", printNodeToString(N.ASTNode, PrintPolicy), indent(-1));
856 claimTokensFor(N.ASTNode, N.Selected);
857 if (N.Selected == NoTokens)
859 if (N.Selected || !N.Children.empty()) {
861 N.Parent->Children.push_back(&N);
864 assert(&N == &Nodes.back());
873 SourceRange earlySourceRange(
const DynTypedNode &N) {
874 if (
const Decl *VD = N.get<VarDecl>()) {
883 return VD->getLocation();
890 if (
const auto *CDD = N.get<CXXDestructorDecl>())
891 return CDD->getNameInfo().getNamedTypeInfo()->getTypeLoc().getBeginLoc();
892 if (
const auto *ME = N.get<MemberExpr>()) {
893 auto NameInfo = ME->getMemberNameInfo();
894 if (NameInfo.getName().getNameKind() ==
895 DeclarationName::CXXDestructorName)
896 return NameInfo.getNamedTypeInfo()->getTypeLoc().getBeginLoc();
899 return SourceRange();
909 if (
const auto *CCE = N.get<CXXConstructExpr>()) {
910 claimRange(CCE->getParenOrBraceRange(), Result);
915 if (N.get<ExprWithCleanups>())
943 if (
const auto *TL = N.get<TypeLoc>()) {
944 if (
auto PTL = TL->getAs<ParenTypeLoc>()) {
945 claimRange(PTL.getLParenLoc(), Result);
946 claimRange(PTL.getRParenLoc(), Result);
949 if (
auto ATL = TL->getAs<ArrayTypeLoc>()) {
950 claimRange(ATL.getBracketsRange(), Result);
953 if (
auto PTL = TL->getAs<PointerTypeLoc>()) {
954 claimRange(PTL.getStarLoc(), Result);
957 if (
auto FTL = TL->getAs<FunctionTypeLoc>()) {
958 claimRange(SourceRange(FTL.getLParenLoc(), FTL.getEndLoc()), Result);
962 claimRange(getSourceRange(N), Result);
970 for (
const auto &ClaimedRange :
971 UnclaimedExpandedTokens.erase(TokenBuf.expandedTokens(S)))
972 update(Result, SelChecker.test(ClaimedRange));
974 if (Result && Result != NoTokens)
975 dlog(
"{1}hit selection: {0}", S.printToString(SM), indent());
978 std::string indent(
int Offset = 0) {
980 int Amount = int(Stack.size()) + Offset;
982 return std::string(Amount,
' ');
986 const LangOptions &LangOpts;
988 const PrintingPolicy &PrintPolicy;
990 const syntax::TokenBuffer &TokenBuf;
991 std::stack<Node *> Stack;
992 SelectionTester SelChecker;
993 IntervalSet<syntax::Token> UnclaimedExpandedTokens;
994 std::deque<Node> Nodes;
1000 const PrintingPolicy &PP) {
1001 llvm::SmallString<256> Result;
1003 llvm::raw_svector_ostream OS(Result);
1006 auto Pos = Result.find(
'\n');
1007 if (Pos != llvm::StringRef::npos) {
1008 bool MoreText = !llvm::all_of(Result.str().drop_front(Pos), llvm::isSpace);
1011 Result.append(
" …");
1016void SelectionTree::print(llvm::raw_ostream &OS,
const SelectionTree::Node &N,
1023 printNodeKind(OS, N.ASTNode);
1025 for (
const Node *Child : N.Children)
1026 print(OS, *Child, Indent + 2);
1031 llvm::raw_string_ostream OS(S);
1033 return std::move(OS.str());
1040static llvm::SmallVector<std::pair<unsigned, unsigned>, 2>
1042 const auto &SM = Tokens.sourceManager();
1043 SourceLocation Loc = SM.getComposedLoc(SM.getMainFileID(), Offset);
1044 llvm::SmallVector<std::pair<unsigned, unsigned>, 2> Result;
1046 for (
const syntax::Token &Tok :
1047 llvm::reverse(spelledTokensTouching(Loc, Tokens))) {
1048 if (shouldIgnore(Tok))
1050 unsigned Offset = Tokens.sourceManager().getFileOffset(Tok.location());
1051 Result.emplace_back(Offset, Offset + Tok.length());
1054 Result.emplace_back(Offset, Offset);
1059 const syntax::TokenBuffer &Tokens,
1060 unsigned Begin,
unsigned End,
1064 for (std::pair<unsigned, unsigned> Bounds :
pointBounds(Begin, Tokens))
1071 const syntax::TokenBuffer &Tokens,
1072 unsigned int Begin,
unsigned int End) {
1073 std::optional<SelectionTree> Result;
1075 Result = std::move(T);
1078 return std::move(*Result);
1082 unsigned Begin,
unsigned End)
1083 : PrintPolicy(
AST.getLangOpts()) {
1086 const SourceManager &SM =
AST.getSourceManager();
1087 FileID FID = SM.getMainFileID();
1088 PrintPolicy.TerseOutput =
true;
1089 PrintPolicy.IncludeNewlines =
false;
1091 dlog(
"Computing selection for {0}",
1092 SourceRange(SM.getComposedLoc(FID, Begin), SM.getComposedLoc(FID, End))
1093 .printToString(SM));
1094 Nodes = SelectionVisitor::collect(
AST, Tokens, PrintPolicy, Begin, End, FID);
1095 Root = Nodes.empty() ? nullptr : &Nodes.front();
1096 recordMetrics(*
this,
AST.getLangOpts());
1097 dlog(
"Built selection tree\n{0}", *
this);
1101 const Node *Ancestor = Root;
1103 Ancestor = Ancestor->
Children.front();
1107 return Ancestor != Root ? Ancestor :
nullptr;
1111 for (
const Node *CurrentNode =
this; CurrentNode !=
nullptr;
1112 CurrentNode = CurrentNode->
Parent) {
1113 if (
const Decl *Current = CurrentNode->ASTNode.get<Decl>()) {
1114 if (CurrentNode !=
this)
1115 if (
auto *DC = dyn_cast<DeclContext>(Current))
1117 return *Current->getLexicalDeclContext();
1119 if (
const auto *LE = CurrentNode->ASTNode.get<LambdaExpr>())
1120 if (CurrentNode !=
this)
1121 return *LE->getCallOperator();
1123 llvm_unreachable(
"A tree must always be rooted at TranslationUnitDecl.");
1129 return Children.front()->ignoreImplicit();
1135 return Parent->outerImplicit();
static bool createEach(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End, llvm::function_ref< bool(SelectionTree)> Func)
SelectionTree(const SelectionTree &)=delete
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
const Node * commonAncestor() const
bool enabled()
Returns true if there is an active tracer.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
llvm::SmallString< 256 > abbreviatedString(DynTypedNode N, const PrintingPolicy &PP)
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
std::vector< const Attr * > getAttributes(const DynTypedNode &N)
Return attributes attached directly to a node.
static llvm::SmallVector< std::pair< unsigned, unsigned >, 2 > pointBounds(unsigned Offset, const syntax::TokenBuffer &Tokens)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
const Node & ignoreImplicit() const
llvm::SmallVector< const Node * > Children
const DeclContext & getDeclContext() const
const Node & outerImplicit() const
Represents measurements of clangd events, e.g.
@ Distribution
A distribution of values with a meaningful mean and count.