diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b4dedf5e942ad..187412be30fb1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -49,8 +49,8 @@ sycl/source/stream.cpp @againull # Specialization constant sycl/include/CL/sycl/detail/sycl_fe_intrins.hpp @kbobrovs -sycl/include/CL/sycl/detail/spec_constant_impl.hpp @kbobrovs sycl/include/CL/sycl/experimental/spec_constant.hpp @kbobrovs +sycl/source/detail/spec_constant_impl.hpp @kbobrovs # Program manager sycl/source/detail/program_manager @kbobrovs diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index c1b4ea99066fd..3aeabb036ad58 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -14,11 +14,11 @@ jobs: fetch-depth: 2 - name: Get clang-format first - run: sudo apt-get install -yqq clang-format-9 + run: sudo apt-get install -yqq clang-format-10 - name: Run clang-format for the patch run: | - git diff -U0 --no-color ${GITHUB_SHA}^1 ${GITHUB_SHA} -- | ./clang/tools/clang-format/clang-format-diff.py -p1 -binary clang-format-9 > ./clang-format.patch + git diff -U0 --no-color ${GITHUB_SHA}^1 ${GITHUB_SHA} -- | ./clang/tools/clang-format/clang-format-diff.py -p1 -binary clang-format-10 > ./clang-format.patch # Add patch with formatting fixes to CI job artifacts - uses: actions/upload-artifact@v1 diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp index aa860b30fe759..1837ccb6002f9 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp @@ -79,9 +79,8 @@ static QualType getUnqualifiedType(const Expr &E) { } static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) { - llvm::APSInt IntegerConstant; - if (E.isIntegerConstantExpr(IntegerConstant, Ctx)) - return APValue(IntegerConstant); + if (auto IntegerConstant = E.getIntegerConstantExpr(Ctx)) + return APValue(*IntegerConstant); APValue Constant; if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant)) return Constant; diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp index dd0bedd742a40..96b0bb0f9b02d 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp @@ -65,9 +65,9 @@ void ProBoundsConstantArrayIndexCheck::check( if (IndexExpr->isValueDependent()) return; // We check in the specialization. - llvm::APSInt Index; - if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr, - /*isEvaluated=*/true)) { + Optional Index = + IndexExpr->getIntegerConstantExpr(*Result.Context); + if (!Index) { SourceRange BaseRange; if (const auto *ArraySubscriptE = dyn_cast(Matched)) BaseRange = ArraySubscriptE->getBase()->getSourceRange(); @@ -101,9 +101,9 @@ void ProBoundsConstantArrayIndexCheck::check( if (!StdArrayDecl) return; - if (Index.isSigned() && Index.isNegative()) { + if (Index->isSigned() && Index->isNegative()) { diag(Matched->getExprLoc(), "std::array<> index %0 is negative") - << Index.toString(10); + << Index->toString(10); return; } @@ -118,11 +118,11 @@ void ProBoundsConstantArrayIndexCheck::check( // Get uint64_t values, because different bitwidths would lead to an assertion // in APInt::uge. - if (Index.getZExtValue() >= ArraySize.getZExtValue()) { + if (Index->getZExtValue() >= ArraySize.getZExtValue()) { diag(Matched->getExprLoc(), "std::array<> index %0 is past the end of the array " "(which contains %1 elements)") - << Index.toString(10) << ArraySize.toString(10, false); + << Index->toString(10) << ArraySize.toString(10, false); } } diff --git a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp index aef513a527b54..b84e4d525d8cf 100644 --- a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp @@ -500,7 +500,13 @@ static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result, const Expr *&ConstExpr) { std::string CstId = (Id + "-const").str(); ConstExpr = Result.Nodes.getNodeAs(CstId); - return ConstExpr && ConstExpr->isIntegerConstantExpr(Value, *Result.Context); + if (!ConstExpr) + return false; + Optional R = ConstExpr->getIntegerConstantExpr(*Result.Context); + if (!R) + return false; + Value = *R; + return true; } // Overloaded `retrieveIntegerConstantExpr` for compatibility. @@ -673,7 +679,7 @@ static bool retrieveRelationalIntegerConstantExpr( if (const auto *Arg = OverloadedOperatorExpr->getArg(1)) { if (!Arg->isValueDependent() && - !Arg->isIntegerConstantExpr(Value, *Result.Context)) + !Arg->isIntegerConstantExpr(*Result.Context)) return false; } Symbol = OverloadedOperatorExpr->getArg(0); @@ -1265,21 +1271,23 @@ void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) { "left-right-shift-confusion")) { const auto *ShiftingConst = Result.Nodes.getNodeAs("shift-const"); assert(ShiftingConst && "Expr* 'ShiftingConst' is nullptr!"); - APSInt ShiftingValue; + Optional ShiftingValue = + ShiftingConst->getIntegerConstantExpr(*Result.Context); - if (!ShiftingConst->isIntegerConstantExpr(ShiftingValue, *Result.Context)) + if (!ShiftingValue) return; const auto *AndConst = Result.Nodes.getNodeAs("and-const"); assert(AndConst && "Expr* 'AndCont' is nullptr!"); - APSInt AndValue; - if (!AndConst->isIntegerConstantExpr(AndValue, *Result.Context)) + Optional AndValue = + AndConst->getIntegerConstantExpr(*Result.Context); + if (!AndValue) return; // If ShiftingConst is shifted left with more bits than the position of the // leftmost 1 in the bit representation of AndValue, AndConstant is // ineffective. - if (AndValue.getActiveBits() > ShiftingValue) + if (AndValue->getActiveBits() > *ShiftingValue) return; auto Diag = diag(BinaryAndExpr->getOperatorLoc(), diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp index 56d4cceb6002a..c20472c8de593 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp @@ -438,11 +438,12 @@ static bool arrayMatchesBoundExpr(ASTContext *Context, Context->getAsConstantArrayType(ArrayType); if (!ConstType) return false; - llvm::APSInt ConditionSize; - if (!ConditionExpr->isIntegerConstantExpr(ConditionSize, *Context)) + Optional ConditionSize = + ConditionExpr->getIntegerConstantExpr(*Context); + if (!ConditionSize) return false; llvm::APSInt ArraySize(ConstType->getSize()); - return llvm::APSInt::isSameValue(ConditionSize, ArraySize); + return llvm::APSInt::isSameValue(*ConditionSize, ArraySize); } ForLoopIndexUseVisitor::ForLoopIndexUseVisitor(ASTContext *Context, diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 627f40c854368..c6022b2463e85 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -59,24 +59,32 @@ nodeToString(const ast_type_traits::DynTypedNode &N) { } // Helper function for getMembersReferencedViaDependentName() -// which takes a dependent type `T` and heuristically +// which takes a possibly-dependent type `T` and heuristically // resolves it to a CXXRecordDecl in which we can try name lookup. CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) { assert(T); - if (const auto *ICNT = T->getAs()) { + + if (const auto *RT = T->getAs()) + return dyn_cast(RT->getDecl()); + + if (const auto *ICNT = T->getAs()) T = ICNT->getInjectedSpecializationType().getTypePtrOrNull(); - } + if (!T) + return nullptr; + const auto *TST = T->getAs(); if (!TST) return nullptr; + const ClassTemplateDecl *TD = dyn_cast_or_null( TST->getTemplateName().getAsTemplateDecl()); if (!TD) return nullptr; + return TD->getTemplatedDecl(); } -// Given a dependent type and a member name, heuristically resolve the +// Given a tag-decl type and a member name, heuristically resolve the // name to one or more declarations. // The current heuristic is simply to look up the name in the primary // template. This is a heuristic because the template could potentially @@ -154,6 +162,10 @@ const Type *getPointeeType(const Type *T) { return FirstArg.getAsType().getTypePtrOrNull(); } +// Forward declaration, needed as this function is mutually recursive +// with resolveDependentExprToDecls. +const Type *resolveDependentExprToType(const Expr *E); + // Try to heuristically resolve a dependent expression `E` to one // or more declarations that it likely references. std::vector resolveDependentExprToDecls(const Expr *E) { @@ -163,6 +175,17 @@ std::vector resolveDependentExprToDecls(const Expr *E) { if (ME->isArrow()) { BaseType = getPointeeType(BaseType); } + if (!BaseType) + return {}; + if (const auto *BT = BaseType->getAs()) { + // If BaseType is the type of a dependent expression, it's just + // represented as BultinType::Dependent which gives us no information. We + // can get further by analyzing the depedent expression. + Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase(); + if (Base && BT->getKind() == BuiltinType::Dependent) { + BaseType = resolveDependentExprToType(Base); + } + } return getMembersReferencedViaDependentName( BaseType, [ME](ASTContext &) { return ME->getMember(); }, /*IsNonstaticMember=*/true); @@ -173,9 +196,38 @@ std::vector resolveDependentExprToDecls(const Expr *E) { [RE](ASTContext &) { return RE->getDeclName(); }, /*IsNonstaticMember=*/false); } + if (const auto *CE = dyn_cast(E)) { + const auto *CalleeType = resolveDependentExprToType(CE->getCallee()); + if (!CalleeType) + return {}; + if (const auto *FnTypePtr = CalleeType->getAs()) + CalleeType = FnTypePtr->getPointeeType().getTypePtr(); + if (const FunctionType *FnType = CalleeType->getAs()) { + if (const auto *D = + resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) { + return {D}; + } + } + } + if (const auto *ME = dyn_cast(E)) { + return {ME->getMemberDecl()}; + } return {}; } +// Try to heuristically resolve the type of a dependent expression `E`. +const Type *resolveDependentExprToType(const Expr *E) { + std::vector Decls = resolveDependentExprToDecls(E); + if (Decls.size() != 1) // Names an overload set -- just bail. + return nullptr; + if (const auto *TD = dyn_cast(Decls[0])) { + return TD->getTypeForDecl(); + } else if (const auto *VD = dyn_cast(Decls[0])) { + return VD->getType().getTypePtrOrNull(); + } + return nullptr; +} + const NamedDecl *getTemplatePattern(const NamedDecl *D) { if (const CXXRecordDecl *CRD = dyn_cast(D)) { if (const auto *Result = CRD->getTemplateInstantiationPattern()) @@ -235,9 +287,8 @@ const NamedDecl *getTemplatePattern(const NamedDecl *D) { // and both are lossy. We must know upfront what the caller ultimately wants. // // FIXME: improve common dependent scope using name lookup in primary templates. -// e.g. template int foo() { return std::vector().size(); } -// formally size() is unresolved, but the primary template is a good guess. -// This affects: +// We currently handle DependentScopeDeclRefExpr and +// CXXDependentScopeMemberExpr, but some other constructs remain to be handled: // - DependentTemplateSpecializationType, // - DependentNameType // - UnresolvedUsingValueDecl @@ -302,6 +353,8 @@ struct TargetFinder { // Record the underlying decl instead, if allowed. D = USD->getTargetDecl(); Flags |= Rel::Underlying; // continue with the underlying decl. + } else if (const auto *DG = dyn_cast(D)) { + D = DG->getDeducedTemplate(); } if (const Decl *Pat = getTemplatePattern(D)) { @@ -659,6 +712,15 @@ llvm::SmallVector refInDecl(const Decl *D) { /*IsDecl=*/true, {ND}}); } + + void VisitCXXDeductionGuideDecl(const CXXDeductionGuideDecl *DG) { + // The class template name in a deduction guide targets the class + // template. + Refs.push_back(ReferenceLoc{DG->getQualifierLoc(), + DG->getNameInfo().getLoc(), + /*IsDecl=*/false, + {DG->getDeducedTemplate()}}); + } }; Visitor V; diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp index e94a3ca5a0c38..2e3c491d561e7 100644 --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -220,14 +220,26 @@ class SelectionTester { SelFirst, AllSpelledTokens.end(), [&](const syntax::Token &Tok) { return SM.getFileOffset(Tok.location()) < SelEnd; }); + auto Sel = llvm::makeArrayRef(SelFirst, SelLimit); + // Find which of these are preprocessed to nothing and should be ignored. + std::vector PPIgnored(Sel.size(), false); + for (const syntax::TokenBuffer::Expansion &X : + Buf.expansionsOverlapping(Sel)) { + if (X.Expanded.empty()) { + for (const syntax::Token &Tok : X.Spelled) { + if (&Tok >= SelFirst && &Tok < SelLimit) + PPIgnored[&Tok - SelFirst] = true; + } + } + } // Precompute selectedness and offset for selected spelled tokens. - for (const syntax::Token *T = SelFirst; T < SelLimit; ++T) { - if (shouldIgnore(*T)) + for (unsigned I = 0; I < Sel.size(); ++I) { + if (shouldIgnore(Sel[I]) || PPIgnored[I]) continue; SpelledTokens.emplace_back(); Tok &S = SpelledTokens.back(); - S.Offset = SM.getFileOffset(T->location()); - if (S.Offset >= SelBegin && S.Offset + T->length() <= SelEnd) + S.Offset = SM.getFileOffset(Sel[I].location()); + if (S.Offset >= SelBegin && S.Offset + Sel[I].length() <= SelEnd) S.Selected = SelectionTree::Complete; else S.Selected = SelectionTree::Partial; diff --git a/clang-tools-extra/clangd/URI.cpp b/clang-tools-extra/clangd/URI.cpp index 2061a5601c58f..fad93143a30dd 100644 --- a/clang-tools-extra/clangd/URI.cpp +++ b/clang-tools-extra/clangd/URI.cpp @@ -26,6 +26,15 @@ inline llvm::Error make_string_error(const llvm::Twine &Message) { llvm::inconvertibleErrorCode()); } +bool isWindowsPath(llvm::StringRef Path) { + return Path.size() > 1 && llvm::isAlpha(Path[0]) && Path[1] == ':'; +} + +bool isNetworkPath(llvm::StringRef Path) { + return Path.size() > 2 && Path[0] == Path[1] && + llvm::sys::path::is_separator(Path[0]); +} + /// This manages file paths in the file system. All paths in the scheme /// are absolute (with leading '/'). /// Note that this scheme is hardcoded into the library and not registered in @@ -33,28 +42,40 @@ inline llvm::Error make_string_error(const llvm::Twine &Message) { class FileSystemScheme : public URIScheme { public: llvm::Expected - getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body, + getAbsolutePath(llvm::StringRef Authority, llvm::StringRef Body, llvm::StringRef /*HintPath*/) const override { if (!Body.startswith("/")) return make_string_error("File scheme: expect body to be an absolute " "path starting with '/': " + Body); - // For Windows paths e.g. /X: - if (Body.size() > 2 && Body[0] == '/' && Body[2] == ':') + llvm::SmallString<128> Path; + if (!Authority.empty()) { + // Windows UNC paths e.g. file://server/share => \\server\share + ("//" + Authority).toVector(Path); + } else if (isWindowsPath(Body.substr(1))) { + // Windows paths e.g. file:///X:/path => X:\path Body.consume_front("/"); - llvm::SmallVector Path(Body.begin(), Body.end()); + } + Path.append(Body); llvm::sys::path::native(Path); - return std::string(Path.begin(), Path.end()); + return std::string(Path); } llvm::Expected uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override { std::string Body; - // For Windows paths e.g. X: - if (AbsolutePath.size() > 1 && AbsolutePath[1] == ':') + llvm::StringRef Authority; + llvm::StringRef Root = llvm::sys::path::root_name(AbsolutePath); + if (isNetworkPath(Root)) { + // Windows UNC paths e.g. \\server\share => file://server/share + Authority = Root.drop_front(2); + AbsolutePath.consume_front(Root); + } else if (isWindowsPath(Root)) { + // Windows paths e.g. X:\path => file:///X:/path Body = "/"; + } Body += llvm::sys::path::convert_to_slash(AbsolutePath); - return URI("file", /*Authority=*/"", Body); + return URI("file", Authority, Body); } }; @@ -96,13 +117,13 @@ bool shouldEscape(unsigned char C) { void percentEncode(llvm::StringRef Content, std::string &Out) { std::string Result; for (unsigned char C : Content) - if (shouldEscape(C)) - { + if (shouldEscape(C)) { Out.push_back('%'); Out.push_back(llvm::hexdigit(C / 16)); Out.push_back(llvm::hexdigit(C % 16)); - } else - { Out.push_back(C); } + } else { + Out.push_back(C); + } } /// Decodes a string according to percent-encoding. diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index c208e953f2ab7..aeff7ebc32a26 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1183,23 +1183,24 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LocatedSymbol &S) { // FIXME(nridge): Reduce duplication between this function and declToSym(). static llvm::Optional -declToTypeHierarchyItem(ASTContext &Ctx, const NamedDecl &ND, - const syntax::TokenBuffer &TB) { +declToTypeHierarchyItem(ASTContext &Ctx, const NamedDecl &ND) { auto &SM = Ctx.getSourceManager(); SourceLocation NameLoc = nameLocation(ND, Ctx.getSourceManager()); + SourceLocation BeginLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getBeginLoc())); + SourceLocation EndLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getEndLoc())); + const auto DeclRange = + toHalfOpenFileRange(SM, Ctx.getLangOpts(), {BeginLoc, EndLoc}); + if (!DeclRange) + return llvm::None; auto FilePath = getCanonicalPath(SM.getFileEntryForID(SM.getFileID(NameLoc)), SM); auto TUPath = getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM); if (!FilePath || !TUPath) return llvm::None; // Not useful without a uri. - auto DeclToks = TB.spelledForExpanded(TB.expandedTokens(ND.getSourceRange())); - if (!DeclToks || DeclToks->empty()) - return llvm::None; - - auto NameToks = TB.spelledForExpanded(TB.expandedTokens(NameLoc)); - if (!NameToks || NameToks->empty()) - return llvm::None; + Position NameBegin = sourceLocToPosition(SM, NameLoc); + Position NameEnd = sourceLocToPosition( + SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts())); index::SymbolInfo SymInfo = index::getSymbolInfo(&ND); // FIXME: this is not classifying constructors, destructors and operators @@ -1210,12 +1211,9 @@ declToTypeHierarchyItem(ASTContext &Ctx, const NamedDecl &ND, THI.name = printName(Ctx, ND); THI.kind = SK; THI.deprecated = ND.isDeprecated(); - THI.range = halfOpenToRange( - SM, syntax::Token::range(SM, DeclToks->front(), DeclToks->back()) - .toCharRange(SM)); - THI.selectionRange = halfOpenToRange( - SM, syntax::Token::range(SM, NameToks->front(), NameToks->back()) - .toCharRange(SM)); + THI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()), + sourceLocToPosition(SM, DeclRange->getEnd())}; + THI.selectionRange = Range{NameBegin, NameEnd}; if (!THI.range.contains(THI.selectionRange)) { // 'selectionRange' must be contained in 'range', so in cases where clang // reports unrelated ranges we need to reconcile somehow. @@ -1282,8 +1280,7 @@ using RecursionProtectionSet = llvm::SmallSet; static void fillSuperTypes(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx, std::vector &SuperTypes, - RecursionProtectionSet &RPSet, - const syntax::TokenBuffer &TB) { + RecursionProtectionSet &RPSet) { // typeParents() will replace dependent template specializations // with their class template, so to avoid infinite recursion for // certain types of hierarchies, keep the templates encountered @@ -1298,9 +1295,9 @@ static void fillSuperTypes(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx, for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) { if (Optional ParentSym = - declToTypeHierarchyItem(ASTCtx, *ParentDecl, TB)) { + declToTypeHierarchyItem(ASTCtx, *ParentDecl)) { ParentSym->parents.emplace(); - fillSuperTypes(*ParentDecl, ASTCtx, *ParentSym->parents, RPSet, TB); + fillSuperTypes(*ParentDecl, ASTCtx, *ParentSym->parents, RPSet); SuperTypes.emplace_back(std::move(*ParentSym)); } } @@ -1404,7 +1401,7 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, return llvm::None; Optional Result = - declToTypeHierarchyItem(AST.getASTContext(), *CXXRD, AST.getTokens()); + declToTypeHierarchyItem(AST.getASTContext(), *CXXRD); if (!Result) return Result; @@ -1413,8 +1410,7 @@ getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, Result->parents.emplace(); RecursionProtectionSet RPSet; - fillSuperTypes(*CXXRD, AST.getASTContext(), *Result->parents, RPSet, - AST.getTokens()); + fillSuperTypes(*CXXRD, AST.getASTContext(), *Result->parents, RPSet); } if ((Direction == TypeHierarchyDirection::Children || diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp index b502dfb03aec3..6c11399c87b68 100644 --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -373,16 +373,12 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name, index::SymbolRoleSet Roles, SourceLocation Loc) { assert(PP.get()); - - const auto &SM = PP->getSourceManager(); - auto DefLoc = MI->getDefinitionLoc(); - auto SpellingLoc = SM.getSpellingLoc(Loc); - bool IsMainFileSymbol = SM.isInMainFile(SM.getExpansionLoc(DefLoc)); - // Builtin macros don't have useful locations and aren't needed in completion. if (MI->isBuiltinMacro()) return true; + const auto &SM = PP->getSourceManager(); + auto DefLoc = MI->getDefinitionLoc(); // Also avoid storing predefined macros like __DBL_MIN__. if (SM.isWrittenInBuiltinFile(DefLoc)) return true; @@ -391,8 +387,13 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name, if (!ID) return true; + auto SpellingLoc = SM.getSpellingLoc(Loc); + bool IsMainFileOnly = + SM.isInMainFile(SM.getExpansionLoc(DefLoc)) && + !isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(), + ASTCtx->getLangOpts()); // Do not store references to main-file macros. - if ((static_cast(Opts.RefFilter) & Roles) && !IsMainFileSymbol && + if ((static_cast(Opts.RefFilter) & Roles) && !IsMainFileOnly && (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) MacroRefs[*ID].push_back({Loc, Roles}); @@ -401,7 +402,7 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name, return true; // Skip main-file macros if we are not collecting them. - if (IsMainFileSymbol && !Opts.CollectMainFileSymbols) + if (IsMainFileOnly && !Opts.CollectMainFileSymbols) return false; // Mark the macro as referenced if this is a reference coming from the main @@ -425,11 +426,12 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name, Symbol S; S.ID = std::move(*ID); S.Name = Name->getName(); - if (!IsMainFileSymbol) { + if (!IsMainFileOnly) { S.Flags |= Symbol::IndexedForCodeCompletion; S.Flags |= Symbol::VisibleOutsideFile; } S.SymInfo = index::getSymbolInfoForMacro(*MI); + S.Origin = Opts.Origin; std::string FileURI; // FIXME: use the result to filter out symbols. shouldIndexFile(SM.getFileID(Loc)); diff --git a/clang-tools-extra/clangd/index/remote/Client.cpp b/clang-tools-extra/clangd/index/remote/Client.cpp index b468831930765..35ce84068f406 100644 --- a/clang-tools-extra/clangd/index/remote/Client.cpp +++ b/clang-tools-extra/clangd/index/remote/Client.cpp @@ -29,16 +29,6 @@ class IndexClient : public clangd::SymbolIndex { using StreamingCall = std::unique_ptr> ( remote::SymbolIndex::Stub::*)(grpc::ClientContext *, const RequestT &); - template - RequestT serializeRequest(ClangdRequestT Request) const { - return toProtobuf(Request); - } - - template <> - FuzzyFindRequest serializeRequest(clangd::FuzzyFindRequest Request) const { - return toProtobuf(Request, ProjectRoot); - } - template bool streamRPC(ClangdRequestT Request, @@ -46,24 +36,23 @@ class IndexClient : public clangd::SymbolIndex { CallbackT Callback) const { bool FinalResult = false; trace::Span Tracer(RequestT::descriptor()->name()); - const auto RPCRequest = serializeRequest(Request); + const auto RPCRequest = ProtobufMarshaller->toProtobuf(Request); grpc::ClientContext Context; std::chrono::system_clock::time_point Deadline = std::chrono::system_clock::now() + DeadlineWaitingTime; Context.set_deadline(Deadline); auto Reader = (Stub.get()->*RPCCall)(&Context, RPCRequest); - llvm::BumpPtrAllocator Arena; - llvm::UniqueStringSaver Strings(Arena); ReplyT Reply; while (Reader->Read(&Reply)) { if (!Reply.has_stream_result()) { FinalResult = Reply.final_result(); continue; } - auto Response = - fromProtobuf(Reply.stream_result(), &Strings, ProjectRoot); - if (!Response) + auto Response = ProtobufMarshaller->fromProtobuf(Reply.stream_result()); + if (!Response) { elog("Received invalid {0}", ReplyT::descriptor()->name()); + continue; + } Callback(*Response); } SPAN_ATTACH(Tracer, "status", Reader->Finish().ok()); @@ -74,7 +63,9 @@ class IndexClient : public clangd::SymbolIndex { IndexClient( std::shared_ptr Channel, llvm::StringRef ProjectRoot, std::chrono::milliseconds DeadlineTime = std::chrono::milliseconds(1000)) - : Stub(remote::SymbolIndex::NewStub(Channel)), ProjectRoot(ProjectRoot), + : Stub(remote::SymbolIndex::NewStub(Channel)), + ProtobufMarshaller(new Marshaller(/*RemoteIndexRoot=*/"", + /*LocalIndexRoot=*/ProjectRoot)), DeadlineWaitingTime(DeadlineTime) {} void lookup(const clangd::LookupRequest &Request, @@ -105,7 +96,7 @@ class IndexClient : public clangd::SymbolIndex { private: std::unique_ptr Stub; - std::string ProjectRoot; + std::unique_ptr ProtobufMarshaller; // Each request will be terminated if it takes too long. std::chrono::milliseconds DeadlineWaitingTime; }; diff --git a/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp b/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp index db895bf039bab..b6c83c9740727 100644 --- a/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp +++ b/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp @@ -30,101 +30,28 @@ namespace clang { namespace clangd { namespace remote { -namespace { - -clangd::SymbolLocation::Position fromProtobuf(const Position &Message) { - clangd::SymbolLocation::Position Result; - Result.setColumn(static_cast(Message.column())); - Result.setLine(static_cast(Message.line())); - return Result; -} - -Position toProtobuf(const clangd::SymbolLocation::Position &Position) { - remote::Position Result; - Result.set_column(Position.column()); - Result.set_line(Position.line()); - return Result; -} - -clang::index::SymbolInfo fromProtobuf(const SymbolInfo &Message) { - clang::index::SymbolInfo Result; - Result.Kind = static_cast(Message.kind()); - Result.SubKind = static_cast(Message.subkind()); - Result.Lang = static_cast(Message.language()); - Result.Properties = - static_cast(Message.properties()); - return Result; -} - -SymbolInfo toProtobuf(const clang::index::SymbolInfo &Info) { - SymbolInfo Result; - Result.set_kind(static_cast(Info.Kind)); - Result.set_subkind(static_cast(Info.SubKind)); - Result.set_language(static_cast(Info.Lang)); - Result.set_properties(static_cast(Info.Properties)); - return Result; -} - -llvm::Optional -fromProtobuf(const SymbolLocation &Message, llvm::UniqueStringSaver *Strings, - llvm::StringRef IndexRoot) { - clangd::SymbolLocation Location; - auto URIString = relativePathToURI(Message.file_path(), IndexRoot); - if (!URIString) - return llvm::None; - Location.FileURI = Strings->save(*URIString).begin(); - Location.Start = fromProtobuf(Message.start()); - Location.End = fromProtobuf(Message.end()); - return Location; -} - -llvm::Optional -toProtobuf(const clangd::SymbolLocation &Location, llvm::StringRef IndexRoot) { - remote::SymbolLocation Result; - auto RelativePath = uriToRelativePath(Location.FileURI, IndexRoot); - if (!RelativePath) - return llvm::None; - *Result.mutable_file_path() = *RelativePath; - *Result.mutable_start() = toProtobuf(Location.Start); - *Result.mutable_end() = toProtobuf(Location.End); - return Result; -} - -llvm::Optional -toProtobuf(const clangd::Symbol::IncludeHeaderWithReferences &IncludeHeader, - llvm::StringRef IndexRoot) { - HeaderWithReferences Result; - Result.set_references(IncludeHeader.References); - const std::string Header = IncludeHeader.IncludeHeader.str(); - if (isLiteralInclude(Header)) { - Result.set_header(Header); - return Result; +Marshaller::Marshaller(llvm::StringRef RemoteIndexRoot, + llvm::StringRef LocalIndexRoot) + : Strings(Arena) { + if (!RemoteIndexRoot.empty()) { + assert(llvm::sys::path::is_absolute(RemoteIndexRoot)); + assert(RemoteIndexRoot == + llvm::sys::path::convert_to_slash(RemoteIndexRoot)); + assert(RemoteIndexRoot.endswith(llvm::sys::path::get_separator())); + this->RemoteIndexRoot = RemoteIndexRoot.str(); } - auto RelativePath = uriToRelativePath(Header, IndexRoot); - if (!RelativePath) - return llvm::None; - Result.set_header(*RelativePath); - return Result; -} - -llvm::Optional -fromProtobuf(const HeaderWithReferences &Message, - llvm::UniqueStringSaver *Strings, llvm::StringRef IndexRoot) { - std::string Header = Message.header(); - if (Header.front() != '<' && Header.front() != '"') { - auto URIString = relativePathToURI(Header, IndexRoot); - if (!URIString) - return llvm::None; - Header = *URIString; + if (!LocalIndexRoot.empty()) { + assert(llvm::sys::path::is_absolute(LocalIndexRoot)); + assert(LocalIndexRoot == llvm::sys::path::convert_to_slash(LocalIndexRoot)); + assert(LocalIndexRoot.endswith(llvm::sys::path::get_separator())); + this->LocalIndexRoot = LocalIndexRoot.str(); } - return clangd::Symbol::IncludeHeaderWithReferences{Strings->save(Header), - Message.references()}; + assert(!RemoteIndexRoot.empty() || !LocalIndexRoot.empty()); } -} // namespace - -clangd::FuzzyFindRequest fromProtobuf(const FuzzyFindRequest *Request, - llvm::StringRef IndexRoot) { +clangd::FuzzyFindRequest +Marshaller::fromProtobuf(const FuzzyFindRequest *Request) { + assert(RemoteIndexRoot); clangd::FuzzyFindRequest Result; Result.Query = Request->query(); for (const auto &Scope : Request->scopes()) @@ -134,7 +61,7 @@ clangd::FuzzyFindRequest fromProtobuf(const FuzzyFindRequest *Request, Result.Limit = Request->limit(); Result.RestrictForCodeCompletion = Request->restricted_for_code_completion(); for (const auto &Path : Request->proximity_paths()) { - llvm::SmallString<256> LocalPath = llvm::StringRef(IndexRoot); + llvm::SmallString<256> LocalPath = llvm::StringRef(*RemoteIndexRoot); llvm::sys::path::append(LocalPath, Path); Result.ProximityPaths.push_back(std::string(LocalPath)); } @@ -143,34 +70,35 @@ clangd::FuzzyFindRequest fromProtobuf(const FuzzyFindRequest *Request, return Result; } -llvm::Optional fromProtobuf(const Symbol &Message, - llvm::UniqueStringSaver *Strings, - llvm::StringRef IndexRoot) { - if (!Message.has_info() || !Message.has_definition() || - !Message.has_canonical_declaration()) { - elog("Cannot convert Symbol from Protobuf: {0}", - Message.ShortDebugString()); +llvm::Optional Marshaller::fromProtobuf(const Symbol &Message) { + if (!Message.has_info() || !Message.has_canonical_declaration()) { + elog("Cannot convert Symbol from protobuf (missing info, definition or " + "declaration): {0}", + Message.DebugString()); return llvm::None; } clangd::Symbol Result; auto ID = SymbolID::fromStr(Message.id()); if (!ID) { - elog("Cannot parse SymbolID {0} given Protobuf: {1}", ID.takeError(), - Message.ShortDebugString()); + elog("Cannot parse SymbolID {0} given protobuf: {1}", ID.takeError(), + Message.DebugString()); return llvm::None; } Result.ID = *ID; Result.SymInfo = fromProtobuf(Message.info()); Result.Name = Message.name(); Result.Scope = Message.scope(); - auto Definition = fromProtobuf(Message.definition(), Strings, IndexRoot); - if (!Definition) - return llvm::None; - Result.Definition = *Definition; - auto Declaration = - fromProtobuf(Message.canonical_declaration(), Strings, IndexRoot); - if (!Declaration) + if (Message.has_definition()) { + auto Definition = fromProtobuf(Message.definition()); + if (Definition) + Result.Definition = *Definition; + } + auto Declaration = fromProtobuf(Message.canonical_declaration()); + if (!Declaration) { + elog("Cannot convert Symbol from protobuf (invalid declaration): {0}", + Message.DebugString()); return llvm::None; + } Result.CanonicalDeclaration = *Declaration; Result.References = Message.references(); Result.Origin = static_cast(Message.origin()); @@ -181,39 +109,44 @@ llvm::Optional fromProtobuf(const Symbol &Message, Result.ReturnType = Message.return_type(); Result.Type = Message.type(); for (const auto &Header : Message.headers()) { - auto SerializedHeader = fromProtobuf(Header, Strings, IndexRoot); + auto SerializedHeader = fromProtobuf(Header); if (SerializedHeader) Result.IncludeHeaders.push_back(*SerializedHeader); + else + elog("Cannot convert HeaderWithIncludes from protobuf: {0}", + Header.DebugString()); } Result.Flags = static_cast(Message.flags()); return Result; } -llvm::Optional fromProtobuf(const Ref &Message, - llvm::UniqueStringSaver *Strings, - llvm::StringRef IndexRoot) { +llvm::Optional Marshaller::fromProtobuf(const Ref &Message) { if (!Message.has_location()) { - elog("Cannot convert Ref from Protobuf: {}", Message.ShortDebugString()); + elog("Cannot convert Ref from protobuf (missing location): {0}", + Message.DebugString()); return llvm::None; } clangd::Ref Result; - auto Location = fromProtobuf(Message.location(), Strings, IndexRoot); - if (!Location) + auto Location = fromProtobuf(Message.location()); + if (!Location) { + elog("Cannot convert Ref from protobuf (invalid location): {0}", + Message.DebugString()); return llvm::None; + } Result.Location = *Location; Result.Kind = static_cast(Message.kind()); return Result; } -LookupRequest toProtobuf(const clangd::LookupRequest &From) { +LookupRequest Marshaller::toProtobuf(const clangd::LookupRequest &From) { LookupRequest RPCRequest; for (const auto &SymbolID : From.IDs) RPCRequest.add_ids(SymbolID.str()); return RPCRequest; } -FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From, - llvm::StringRef IndexRoot) { +FuzzyFindRequest Marshaller::toProtobuf(const clangd::FuzzyFindRequest &From) { + assert(LocalIndexRoot); FuzzyFindRequest RPCRequest; RPCRequest.set_query(From.Query); for (const auto &Scope : From.Scopes) @@ -224,7 +157,8 @@ FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From, RPCRequest.set_restricted_for_code_completion(From.RestrictForCodeCompletion); for (const auto &Path : From.ProximityPaths) { llvm::SmallString<256> RelativePath = llvm::StringRef(Path); - if (llvm::sys::path::replace_path_prefix(RelativePath, IndexRoot, "")) + if (llvm::sys::path::replace_path_prefix(RelativePath, *LocalIndexRoot, + "")) RPCRequest.add_proximity_paths(llvm::sys::path::convert_to_slash( RelativePath, llvm::sys::path::Style::posix)); } @@ -233,7 +167,7 @@ FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From, return RPCRequest; } -RefsRequest toProtobuf(const clangd::RefsRequest &From) { +RefsRequest Marshaller::toProtobuf(const clangd::RefsRequest &From) { RefsRequest RPCRequest; for (const auto &ID : From.IDs) RPCRequest.add_ids(ID.str()); @@ -243,18 +177,28 @@ RefsRequest toProtobuf(const clangd::RefsRequest &From) { return RPCRequest; } -Symbol toProtobuf(const clangd::Symbol &From, llvm::StringRef IndexRoot) { +llvm::Optional Marshaller::toProtobuf(const clangd::Symbol &From) { Symbol Result; Result.set_id(From.ID.str()); *Result.mutable_info() = toProtobuf(From.SymInfo); Result.set_name(From.Name.str()); - auto Definition = toProtobuf(From.Definition, IndexRoot); - if (Definition) + if (*From.Definition.FileURI) { + auto Definition = toProtobuf(From.Definition); + if (!Definition) { + elog("Can not convert Symbol to protobuf (invalid definition) {0}: {1}", + From, From.Definition); + return llvm::None; + } *Result.mutable_definition() = *Definition; + } Result.set_scope(From.Scope.str()); - auto Declaration = toProtobuf(From.CanonicalDeclaration, IndexRoot); - if (Declaration) - *Result.mutable_canonical_declaration() = *Declaration; + auto Declaration = toProtobuf(From.CanonicalDeclaration); + if (!Declaration) { + elog("Can not convert Symbol to protobuf (invalid declaration) {0}: {1}", + From, From.CanonicalDeclaration); + return llvm::None; + } + *Result.mutable_canonical_declaration() = *Declaration; Result.set_references(From.References); Result.set_origin(static_cast(From.Origin)); Result.set_signature(From.Signature.str()); @@ -265,9 +209,12 @@ Symbol toProtobuf(const clangd::Symbol &From, llvm::StringRef IndexRoot) { Result.set_return_type(From.ReturnType.str()); Result.set_type(From.Type.str()); for (const auto &Header : From.IncludeHeaders) { - auto Serialized = toProtobuf(Header, IndexRoot); - if (!Serialized) + auto Serialized = toProtobuf(Header); + if (!Serialized) { + elog("Can not convert IncludeHeaderWithReferences to protobuf: {0}", + Header.IncludeHeader); continue; + } auto *NextHeader = Result.add_headers(); *NextHeader = *Serialized; } @@ -275,50 +222,38 @@ Symbol toProtobuf(const clangd::Symbol &From, llvm::StringRef IndexRoot) { return Result; } -// FIXME(kirillbobyrev): A reference without location is invalid. -// llvm::Optional here and on the server side? -Ref toProtobuf(const clangd::Ref &From, llvm::StringRef IndexRoot) { +llvm::Optional Marshaller::toProtobuf(const clangd::Ref &From) { Ref Result; Result.set_kind(static_cast(From.Kind)); - auto Location = toProtobuf(From.Location, IndexRoot); - if (Location) - *Result.mutable_location() = *Location; + auto Location = toProtobuf(From.Location); + if (!Location) { + elog("Can not convert Reference to protobuf (invalid location) {0}: {1}", + From, From.Location); + return llvm::None; + } + *Result.mutable_location() = *Location; return Result; } -llvm::Optional relativePathToURI(llvm::StringRef RelativePath, - llvm::StringRef IndexRoot) { +llvm::Optional +Marshaller::relativePathToURI(llvm::StringRef RelativePath) { + assert(LocalIndexRoot); assert(RelativePath == llvm::sys::path::convert_to_slash( RelativePath, llvm::sys::path::Style::posix)); - assert(IndexRoot == llvm::sys::path::convert_to_slash(IndexRoot)); - assert(IndexRoot.endswith(llvm::sys::path::get_separator())); - if (RelativePath.empty()) - return std::string(); - if (llvm::sys::path::is_absolute(RelativePath)) { - elog("Remote index client got absolute path from server: {0}", - RelativePath); + if (RelativePath.empty()) { return llvm::None; } - if (llvm::sys::path::is_relative(IndexRoot)) { - elog("Remote index client got a relative path as index root: {0}", - IndexRoot); + if (llvm::sys::path::is_absolute(RelativePath)) { return llvm::None; } - llvm::SmallString<256> FullPath = IndexRoot; + llvm::SmallString<256> FullPath = llvm::StringRef(*LocalIndexRoot); llvm::sys::path::append(FullPath, RelativePath); auto Result = URI::createFile(FullPath); return Result.toString(); } -llvm::Optional uriToRelativePath(llvm::StringRef URI, - llvm::StringRef IndexRoot) { - assert(IndexRoot.endswith(llvm::sys::path::get_separator())); - assert(IndexRoot == llvm::sys::path::convert_to_slash(IndexRoot)); - assert(!IndexRoot.empty()); - if (llvm::sys::path::is_relative(IndexRoot)) { - elog("Index root {0} is not absolute path", IndexRoot); - return llvm::None; - } +llvm::Optional Marshaller::uriToRelativePath(llvm::StringRef URI) { + assert(RemoteIndexRoot); auto ParsedURI = URI::parse(URI); if (!ParsedURI) { elog("Remote index got bad URI from client {0}: {1}", URI, @@ -326,14 +261,10 @@ llvm::Optional uriToRelativePath(llvm::StringRef URI, return llvm::None; } if (ParsedURI->scheme() != "file") { - elog("Remote index got URI with scheme other than \"file\" {0}: {1}", URI, - ParsedURI->scheme()); return llvm::None; } llvm::SmallString<256> Result = ParsedURI->body(); - if (!llvm::sys::path::replace_path_prefix(Result, IndexRoot, "")) { - elog("Can not get relative path from the URI {0} given the index root {1}", - URI, IndexRoot); + if (!llvm::sys::path::replace_path_prefix(Result, *RemoteIndexRoot, "")) { return llvm::None; } // Make sure the result has UNIX slashes. @@ -341,6 +272,94 @@ llvm::Optional uriToRelativePath(llvm::StringRef URI, llvm::sys::path::Style::posix); } +clangd::SymbolLocation::Position +Marshaller::fromProtobuf(const Position &Message) { + clangd::SymbolLocation::Position Result; + Result.setColumn(static_cast(Message.column())); + Result.setLine(static_cast(Message.line())); + return Result; +} + +Position +Marshaller::toProtobuf(const clangd::SymbolLocation::Position &Position) { + remote::Position Result; + Result.set_column(Position.column()); + Result.set_line(Position.line()); + return Result; +} + +clang::index::SymbolInfo Marshaller::fromProtobuf(const SymbolInfo &Message) { + clang::index::SymbolInfo Result; + Result.Kind = static_cast(Message.kind()); + Result.SubKind = static_cast(Message.subkind()); + Result.Lang = static_cast(Message.language()); + Result.Properties = + static_cast(Message.properties()); + return Result; +} + +SymbolInfo Marshaller::toProtobuf(const clang::index::SymbolInfo &Info) { + SymbolInfo Result; + Result.set_kind(static_cast(Info.Kind)); + Result.set_subkind(static_cast(Info.SubKind)); + Result.set_language(static_cast(Info.Lang)); + Result.set_properties(static_cast(Info.Properties)); + return Result; +} + +llvm::Optional +Marshaller::fromProtobuf(const SymbolLocation &Message) { + clangd::SymbolLocation Location; + auto URIString = relativePathToURI(Message.file_path()); + if (!URIString) + return llvm::None; + Location.FileURI = Strings.save(*URIString).begin(); + Location.Start = fromProtobuf(Message.start()); + Location.End = fromProtobuf(Message.end()); + return Location; +} + +llvm::Optional +Marshaller::toProtobuf(const clangd::SymbolLocation &Location) { + remote::SymbolLocation Result; + auto RelativePath = uriToRelativePath(Location.FileURI); + if (!RelativePath) + return llvm::None; + *Result.mutable_file_path() = *RelativePath; + *Result.mutable_start() = toProtobuf(Location.Start); + *Result.mutable_end() = toProtobuf(Location.End); + return Result; +} + +llvm::Optional Marshaller::toProtobuf( + const clangd::Symbol::IncludeHeaderWithReferences &IncludeHeader) { + HeaderWithReferences Result; + Result.set_references(IncludeHeader.References); + const std::string Header = IncludeHeader.IncludeHeader.str(); + if (isLiteralInclude(Header)) { + Result.set_header(Header); + return Result; + } + auto RelativePath = uriToRelativePath(Header); + if (!RelativePath) + return llvm::None; + Result.set_header(*RelativePath); + return Result; +} + +llvm::Optional +Marshaller::fromProtobuf(const HeaderWithReferences &Message) { + std::string Header = Message.header(); + if (Header.front() != '<' && Header.front() != '"') { + auto URIString = relativePathToURI(Header); + if (!URIString) + return llvm::None; + Header = *URIString; + } + return clangd::Symbol::IncludeHeaderWithReferences{Strings.save(Header), + Message.references()}; +} + } // namespace remote } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.h b/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.h index 86cc8fa272fc5..9129cff24db57 100644 --- a/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.h +++ b/clang-tools-extra/clangd/index/remote/marshalling/Marshalling.h @@ -5,31 +5,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// Marshalling provides translation between native clangd types into the -// Protobuf-generated classes. Most translations are 1-to-1 and wrap variables -// into appropriate Protobuf types. -// -// A notable exception is URI translation. Because paths to files are different -// on indexing machine and client machine -// ("/remote/machine/projects/llvm-project/llvm/include/HelloWorld.h" versus -// "/usr/local/username/llvm-project/llvm/include/HelloWorld.h"), they need to -// be converted appropriately. Remote machine strips the prefix from the -// absolute path and passes paths relative to the project root over the wire -// ("include/HelloWorld.h" in this example). The indexed project root is passed -// to the remote server. Client receives this relative path and constructs a URI -// that points to the relevant file in the filesystem. The relative path is -// appended to IndexRoot to construct the full path and build the final URI. -// -// Index root is the absolute path to the project and includes a trailing slash. -// The relative path passed over the wire has unix slashes. -// -// toProtobuf() functions serialize native clangd types and strip IndexRoot from -// the file paths specific to indexing machine. fromProtobuf() functions -// deserialize clangd types and translate relative paths into machine-native -// URIs. -// -//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_REMOTE_MARSHALLING_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_REMOTE_MARSHALLING_H @@ -43,33 +18,76 @@ namespace clang { namespace clangd { namespace remote { -clangd::FuzzyFindRequest fromProtobuf(const FuzzyFindRequest *Request, - llvm::StringRef IndexRoot); -llvm::Optional fromProtobuf(const Symbol &Message, - llvm::UniqueStringSaver *Strings, - llvm::StringRef IndexRoot); -llvm::Optional fromProtobuf(const Ref &Message, - llvm::UniqueStringSaver *Strings, - llvm::StringRef IndexRoot); +// Marshalling provides an interface for translattion between native clangd +// types into the Protobuf-generated classes. Most translations are 1-to-1 and +// wrap variables into appropriate Protobuf types. +// +/// A notable exception is URI translation. Because paths to files are different +/// on indexing machine and client machine +/// ("/remote/machine/projects/llvm-project/llvm/include/HelloWorld.h" versus +/// "/usr/local/username/llvm-project/llvm/include/HelloWorld.h"), they need to +/// be converted appropriately. Remote machine strips the prefix +/// (RemoteIndexRoot) from the absolute path and passes paths relative to the +/// project root over the wire ("include/HelloWorld.h" in this example). The +/// indexed project root is passed to the remote server. Client receives this +/// relative path and constructs a URI that points to the relevant file in the +/// filesystem. The relative path is appended to LocalIndexRoot to construct the +/// full path and build the final URI. +class Marshaller { +public: + Marshaller() = delete; + Marshaller(llvm::StringRef RemoteIndexRoot, llvm::StringRef LocalIndexRoot); + + clangd::FuzzyFindRequest fromProtobuf(const FuzzyFindRequest *Request); + llvm::Optional fromProtobuf(const Symbol &Message); + llvm::Optional fromProtobuf(const Ref &Message); + + /// toProtobuf() functions serialize native clangd types and strip IndexRoot + /// from the file paths specific to indexing machine. fromProtobuf() functions + /// deserialize clangd types and translate relative paths into machine-native + /// URIs. + LookupRequest toProtobuf(const clangd::LookupRequest &From); + FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From); + RefsRequest toProtobuf(const clangd::RefsRequest &From); + + llvm::Optional toProtobuf(const clangd::Ref &From); + llvm::Optional toProtobuf(const clangd::Symbol &From); -LookupRequest toProtobuf(const clangd::LookupRequest &From); -FuzzyFindRequest toProtobuf(const clangd::FuzzyFindRequest &From, - llvm::StringRef IndexRoot); -RefsRequest toProtobuf(const clangd::RefsRequest &From); + /// Translates \p RelativePath into the absolute path and builds URI for the + /// user machine. This translation happens on the client side with the + /// \p RelativePath received from remote index server and \p IndexRoot is + /// provided by the client. + /// + /// The relative path passed over the wire has unix slashes. + llvm::Optional relativePathToURI(llvm::StringRef RelativePath); + /// Translates a URI from the server's backing index to a relative path + /// suitable to send over the wire to the client. + llvm::Optional uriToRelativePath(llvm::StringRef URI); -Ref toProtobuf(const clangd::Ref &From, llvm::StringRef IndexRoot); -Symbol toProtobuf(const clangd::Symbol &From, llvm::StringRef IndexRoot); +private: + clangd::SymbolLocation::Position fromProtobuf(const Position &Message); + Position toProtobuf(const clangd::SymbolLocation::Position &Position); + clang::index::SymbolInfo fromProtobuf(const SymbolInfo &Message); + SymbolInfo toProtobuf(const clang::index::SymbolInfo &Info); + llvm::Optional + fromProtobuf(const SymbolLocation &Message); + llvm::Optional + toProtobuf(const clangd::SymbolLocation &Location); + llvm::Optional + toProtobuf(const clangd::Symbol::IncludeHeaderWithReferences &IncludeHeader); + llvm::Optional + fromProtobuf(const HeaderWithReferences &Message); -/// Translates \p RelativePath into the absolute path and builds URI for the -/// user machine. This translation happens on the client side with the -/// \p RelativePath received from remote index server and \p IndexRoot is -/// provided by the client. -llvm::Optional relativePathToURI(llvm::StringRef RelativePath, - llvm::StringRef IndexRoot); -/// Translates a URI from the server's backing index to a relative path suitable -/// to send over the wire to the client. -llvm::Optional uriToRelativePath(llvm::StringRef URI, - llvm::StringRef IndexRoot); + /// RemoteIndexRoot and LocalIndexRoot are absolute paths to the project (on + /// remote and local machine respectively) and include a trailing slash. One + /// of them can be missing (if the machines are different they don't know each + /// other's specifics and will only do one-way translation), but both can not + /// be missing at the same time. + llvm::Optional RemoteIndexRoot; + llvm::Optional LocalIndexRoot; + llvm::BumpPtrAllocator Arena; + llvm::UniqueStringSaver Strings; +}; } // namespace remote } // namespace clangd diff --git a/clang-tools-extra/clangd/index/remote/server/Server.cpp b/clang-tools-extra/clangd/index/remote/server/Server.cpp index fecd72806cbc0..07b1c736b6725 100644 --- a/clang-tools-extra/clangd/index/remote/server/Server.cpp +++ b/clang-tools-extra/clangd/index/remote/server/Server.cpp @@ -50,7 +50,9 @@ class RemoteIndexServer final : public SymbolIndex::Service { : Index(std::move(Index)) { llvm::SmallString<256> NativePath = IndexRoot; llvm::sys::path::native(NativePath); - IndexedProjectRoot = std::string(NativePath); + ProtobufMarshaller = std::unique_ptr(new Marshaller( + /*RemoteIndexRoot=*/llvm::StringRef(NativePath), + /*LocalIndexRoot=*/"")); } private: @@ -65,9 +67,11 @@ class RemoteIndexServer final : public SymbolIndex::Service { Req.IDs.insert(*SID); } Index->lookup(Req, [&](const clangd::Symbol &Sym) { + auto SerializedSymbol = ProtobufMarshaller->toProtobuf(Sym); + if (!SerializedSymbol) + return; LookupReply NextMessage; - *NextMessage.mutable_stream_result() = - toProtobuf(Sym, IndexedProjectRoot); + *NextMessage.mutable_stream_result() = *SerializedSymbol; Reply->Write(NextMessage); }); LookupReply LastMessage; @@ -79,11 +83,13 @@ class RemoteIndexServer final : public SymbolIndex::Service { grpc::Status FuzzyFind(grpc::ServerContext *Context, const FuzzyFindRequest *Request, grpc::ServerWriter *Reply) override { - const auto Req = fromProtobuf(Request, IndexedProjectRoot); + const auto Req = ProtobufMarshaller->fromProtobuf(Request); bool HasMore = Index->fuzzyFind(Req, [&](const clangd::Symbol &Sym) { + auto SerializedSymbol = ProtobufMarshaller->toProtobuf(Sym); + if (!SerializedSymbol) + return; FuzzyFindReply NextMessage; - *NextMessage.mutable_stream_result() = - toProtobuf(Sym, IndexedProjectRoot); + *NextMessage.mutable_stream_result() = *SerializedSymbol; Reply->Write(NextMessage); }); FuzzyFindReply LastMessage; @@ -102,8 +108,11 @@ class RemoteIndexServer final : public SymbolIndex::Service { Req.IDs.insert(*SID); } bool HasMore = Index->refs(Req, [&](const clangd::Ref &Reference) { + auto SerializedRef = ProtobufMarshaller->toProtobuf(Reference); + if (!SerializedRef) + return; RefsReply NextMessage; - *NextMessage.mutable_stream_result() = toProtobuf(Reference, IndexRoot); + *NextMessage.mutable_stream_result() = *SerializedRef; Reply->Write(NextMessage); }); RefsReply LastMessage; @@ -113,7 +122,7 @@ class RemoteIndexServer final : public SymbolIndex::Service { } std::unique_ptr Index; - std::string IndexedProjectRoot; + std::unique_ptr ProtobufMarshaller; }; void runServer(std::unique_ptr Index, diff --git a/clang-tools-extra/clangd/test/Inputs/background-index/compile_commands.json b/clang-tools-extra/clangd/test/Inputs/background-index/compile_commands.json.tmpl similarity index 100% rename from clang-tools-extra/clangd/test/Inputs/background-index/compile_commands.json rename to clang-tools-extra/clangd/test/Inputs/background-index/compile_commands.json.tmpl diff --git a/clang-tools-extra/clangd/test/Inputs/background-index/definition.jsonrpc b/clang-tools-extra/clangd/test/Inputs/background-index/definition.jsonrpc.tmpl similarity index 100% rename from clang-tools-extra/clangd/test/Inputs/background-index/definition.jsonrpc rename to clang-tools-extra/clangd/test/Inputs/background-index/definition.jsonrpc.tmpl diff --git a/clang-tools-extra/clangd/test/background-index.test b/clang-tools-extra/clangd/test/background-index.test index 41184443e947a..1983f0957dccf 100644 --- a/clang-tools-extra/clangd/test/background-index.test +++ b/clang-tools-extra/clangd/test/background-index.test @@ -1,23 +1,23 @@ -# We need to splice paths into file:// URIs for this test. -# UNSUPPORTED: windows-msvc - # Use a copy of inputs, as we'll mutate it (as will the background index). -# RUN: rm -rf %t -# RUN: cp -r %S/Inputs/background-index %t +# RUN: rm -rf %/t +# RUN: cp -r %/S/Inputs/background-index %/t # Need to embed the correct temp path in the actual JSON-RPC requests. -# RUN: sed -i -e "s|DIRECTORY|%t|" %t/definition.jsonrpc -# RUN: sed -i -e "s|DIRECTORY|%t|" %t/compile_commands.json +# RUN: sed -e "s|DIRECTORY|%/t|" %/t/definition.jsonrpc.tmpl > %/t/definition.jsonrpc.1 +# RUN: sed -e "s|DIRECTORY|%/t|" %/t/compile_commands.json.tmpl > %/t/compile_commands.json +# On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..." +# (with the extra slash in the front), so we add it here. +# RUN: sed -E -e 's|"file://([A-Z]):/|"file:///\1:/|g' %/t/definition.jsonrpc.1 > %/t/definition.jsonrpc # We're editing bar.cpp, which includes foo.h. # foo() is declared in foo.h and defined in foo.cpp. # The background index should allow us to go-to-definition on foo(). # We should also see indexing progress notifications. -# RUN: clangd -background-index -lit-test < %t/definition.jsonrpc | FileCheck %t/definition.jsonrpc --check-prefixes=CHECK,BUILD +# RUN: clangd -background-index -lit-test < %/t/definition.jsonrpc | FileCheck %/t/definition.jsonrpc --check-prefixes=CHECK,BUILD # Test that the index is writing files in the expected location. -# RUN: ls %t/.cache/clangd/index/foo.cpp.*.idx -# RUN: ls %t/sub_dir/.cache/clangd/index/foo.h.*.idx +# RUN: ls %/t/.cache/clangd/index/foo.cpp.*.idx +# RUN: ls %/t/sub_dir/.cache/clangd/index/foo.h.*.idx # Test the index is read from disk: delete code and restart clangd. -# RUN: rm %t/foo.cpp -# RUN: clangd -background-index -lit-test < %t/definition.jsonrpc | FileCheck %t/definition.jsonrpc --check-prefixes=CHECK,USE +# RUN: rm %/t/foo.cpp +# RUN: clangd -background-index -lit-test < %/t/definition.jsonrpc | FileCheck %/t/definition.jsonrpc --check-prefixes=CHECK,USE diff --git a/clang-tools-extra/clangd/test/did-change-configuration-params.test b/clang-tools-extra/clangd/test/did-change-configuration-params.test index 4aef1011b3707..19d41d0812e23 100644 --- a/clang-tools-extra/clangd/test/did-change-configuration-params.test +++ b/clang-tools-extra/clangd/test/did-change-configuration-params.test @@ -1,5 +1,5 @@ # RUN: clangd -compile_args_from=lsp -lit-test < %s 2> %t | FileCheck -strict-whitespace %s -# RUN: cat %t | FileCheck --check-prefix=ERR %s +# RUN: FileCheck --check-prefix=ERR --input-file=%t %s # UNSUPPORTED: windows-gnu,windows-msvc {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} --- diff --git a/clang-tools-extra/clangd/test/test-uri-windows.test b/clang-tools-extra/clangd/test/test-uri-windows.test index 381c48fafc038..3f03b2985a70f 100644 --- a/clang-tools-extra/clangd/test/test-uri-windows.test +++ b/clang-tools-extra/clangd/test/test-uri-windows.test @@ -1,5 +1,5 @@ # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s -# REQUIRES: windows-gnu || windows-msvc +# UNSUPPORTED: !(windows-gnu || windows-msvc) # Test authority-less URI {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} --- diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index c25e2b7f81037..8ede92c16f7ad 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -13,6 +13,10 @@ include_directories( ${CLANGD_BINARY_DIR} ) +if (CXX_SUPPORTS_SUGGEST_OVERRIDE_FLAG) + add_compile_options("-Wno-suggest-override") +endif() + if(CLANG_BUILT_STANDALONE) # LLVMTestingSupport library is needed for clangd tests. if (EXISTS ${LLVM_MAIN_SRC_DIR}/lib/Testing/Support diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index bf5cc62411b74..f7af77e8b57be 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -376,6 +376,8 @@ TEST_F(TargetDeclTest, ClassTemplate) { {"template<> class Foo", Rel::TemplateInstantiation}, {"template class Foo", Rel::TemplatePattern}); + Flags.push_back("-std=c++17"); // for CTAD tests + Code = R"cpp( // Class template argument deduction template @@ -386,9 +388,20 @@ TEST_F(TargetDeclTest, ClassTemplate) { [[Test]] a(5); } )cpp"; - Flags.push_back("-std=c++17"); EXPECT_DECLS("DeducedTemplateSpecializationTypeLoc", {"struct Test", Rel::TemplatePattern}); + + Code = R"cpp( + // Deduction guide + template + struct Test { + template + Test(I, I); + }; + template + [[Test]](I, I) -> Test; + )cpp"; + EXPECT_DECLS("CXXDeductionGuideDecl", {"template struct Test"}); } TEST_F(TargetDeclTest, Concept) { @@ -548,6 +561,74 @@ TEST_F(TargetDeclTest, OverloadExpr) { EXPECT_DECLS("UnresolvedMemberExpr", "void func(int *)", "void func(char *)"); } +TEST_F(TargetDeclTest, DependentExprs) { + Flags = {"-fno-delayed-template-parsing"}; + + // Heuristic resolution of method of dependent field + Code = R"cpp( + struct A { void foo() {} }; + template + struct B { + A a; + void bar() { + this->a.[[foo]](); + } + }; + )cpp"; + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + + // Similar to above but base expression involves a function call. + Code = R"cpp( + struct A { + void foo() {} + }; + struct B { + A getA(); + }; + template + struct C { + B c; + void bar() { + this->c.getA().[[foo]](); + } + }; + )cpp"; + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + + // Similar to above but uses a function pointer. + Code = R"cpp( + struct A { + void foo() {} + }; + struct B { + using FPtr = A(*)(); + FPtr fptr; + }; + template + struct C { + B c; + void bar() { + this->c.fptr().[[foo]](); + } + }; + )cpp"; + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + + // Base expression involves a member access into this. + Code = R"cpp( + struct Bar { + int aaaa; + }; + template struct Foo { + Bar func(int); + void test() { + func(1).[[aaaa]]; + } + }; + )cpp"; + EXPECT_DECLS("CXXDependentScopeMemberExpr", "int aaaa"); +} + TEST_F(TargetDeclTest, ObjC) { Flags = {"-xobjective-c"}; Code = R"cpp( @@ -705,36 +786,37 @@ class FindExplicitReferencesTest : public ::testing::Test { TEST_F(FindExplicitReferencesTest, All) { std::pair Cases[] = - {// Simple expressions. - {R"cpp( + { + // Simple expressions. + {R"cpp( int global; int func(); void foo(int param) { $0^global = $1^param + $2^func(); } )cpp", - "0: targets = {global}\n" - "1: targets = {param}\n" - "2: targets = {func}\n"}, - {R"cpp( + "0: targets = {global}\n" + "1: targets = {param}\n" + "2: targets = {func}\n"}, + {R"cpp( struct X { int a; }; void foo(X x) { $0^x.$1^a = 10; } )cpp", - "0: targets = {x}\n" - "1: targets = {X::a}\n"}, - {R"cpp( + "0: targets = {x}\n" + "1: targets = {X::a}\n"}, + {R"cpp( // error-ok: testing with broken code int bar(); int foo() { return $0^bar() + $1^bar(42); } )cpp", - "0: targets = {bar}\n" - "1: targets = {bar}\n"}, - // Namespaces and aliases. - {R"cpp( + "0: targets = {bar}\n" + "1: targets = {bar}\n"}, + // Namespaces and aliases. + {R"cpp( namespace ns {} namespace alias = ns; void foo() { @@ -742,19 +824,19 @@ TEST_F(FindExplicitReferencesTest, All) { using namespace $1^alias; } )cpp", - "0: targets = {ns}\n" - "1: targets = {alias}\n"}, - // Using declarations. - {R"cpp( + "0: targets = {ns}\n" + "1: targets = {alias}\n"}, + // Using declarations. + {R"cpp( namespace ns { int global; } void foo() { using $0^ns::$1^global; } )cpp", - "0: targets = {ns}\n" - "1: targets = {ns::global}, qualifier = 'ns::'\n"}, - // Simple types. - {R"cpp( + "0: targets = {ns}\n" + "1: targets = {ns::global}, qualifier = 'ns::'\n"}, + // Simple types. + {R"cpp( struct Struct { int a; }; using Typedef = int; void foo() { @@ -763,13 +845,13 @@ TEST_F(FindExplicitReferencesTest, All) { static_cast<$4^Struct*>(0); } )cpp", - "0: targets = {Struct}\n" - "1: targets = {x}, decl\n" - "2: targets = {Typedef}\n" - "3: targets = {y}, decl\n" - "4: targets = {Struct}\n"}, - // Name qualifiers. - {R"cpp( + "0: targets = {Struct}\n" + "1: targets = {x}, decl\n" + "2: targets = {Typedef}\n" + "3: targets = {y}, decl\n" + "4: targets = {Struct}\n"}, + // Name qualifiers. + {R"cpp( namespace a { namespace b { struct S { typedef int type; }; } } void foo() { $0^a::$1^b::$2^S $3^x; @@ -777,23 +859,23 @@ TEST_F(FindExplicitReferencesTest, All) { $6^S::$7^type $8^y; } )cpp", - "0: targets = {a}\n" - "1: targets = {a::b}, qualifier = 'a::'\n" - "2: targets = {a::b::S}, qualifier = 'a::b::'\n" - "3: targets = {x}, decl\n" - "4: targets = {a}\n" - "5: targets = {a::b}, qualifier = 'a::'\n" - "6: targets = {a::b::S}\n" - "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" - "8: targets = {y}, decl\n"}, - {R"cpp( + "0: targets = {a}\n" + "1: targets = {a::b}, qualifier = 'a::'\n" + "2: targets = {a::b::S}, qualifier = 'a::b::'\n" + "3: targets = {x}, decl\n" + "4: targets = {a}\n" + "5: targets = {a::b}, qualifier = 'a::'\n" + "6: targets = {a::b::S}\n" + "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" + "8: targets = {y}, decl\n"}, + {R"cpp( void foo() { $0^ten: // PRINT "HELLO WORLD!" goto $1^ten; } )cpp", - "0: targets = {ten}, decl\n" - "1: targets = {ten}\n"}, + "0: targets = {ten}, decl\n" + "1: targets = {ten}\n"}, // Simple templates. {R"cpp( template struct vector { using value_type = T; }; @@ -803,12 +885,12 @@ TEST_F(FindExplicitReferencesTest, All) { $2^vector $3^vb; } )cpp", - "0: targets = {vector}\n" - "1: targets = {vi}, decl\n" - "2: targets = {vector}\n" - "3: targets = {vb}, decl\n"}, - // Template type aliases. - {R"cpp( + "0: targets = {vector}\n" + "1: targets = {vi}, decl\n" + "2: targets = {vector}\n" + "3: targets = {vb}, decl\n"}, + // Template type aliases. + {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; template using valias = vector; @@ -817,12 +899,12 @@ TEST_F(FindExplicitReferencesTest, All) { $2^valias $3^vb; } )cpp", - "0: targets = {valias}\n" - "1: targets = {vi}, decl\n" - "2: targets = {valias}\n" - "3: targets = {vb}, decl\n"}, - // Injected class name. - {R"cpp( + "0: targets = {valias}\n" + "1: targets = {vi}, decl\n" + "2: targets = {valias}\n" + "3: targets = {vb}, decl\n"}, + // Injected class name. + {R"cpp( namespace foo { template class $1^Bar { @@ -831,13 +913,13 @@ TEST_F(FindExplicitReferencesTest, All) { }; } )cpp", - "0: targets = {foo::Bar::T}, decl\n" - "1: targets = {foo::Bar}, decl\n" - "2: targets = {foo::Bar}\n" - "3: targets = {foo::Bar::f}, decl\n" - "4: targets = {foo::Bar}\n"}, - // MemberExpr should know their using declaration. - {R"cpp( + "0: targets = {foo::Bar::T}, decl\n" + "1: targets = {foo::Bar}, decl\n" + "2: targets = {foo::Bar}\n" + "3: targets = {foo::Bar::f}, decl\n" + "4: targets = {foo::Bar}\n"}, + // MemberExpr should know their using declaration. + {R"cpp( struct X { void func(int); }; struct Y : X { using X::func; @@ -846,10 +928,10 @@ TEST_F(FindExplicitReferencesTest, All) { $0^y.$1^func(1); } )cpp", - "0: targets = {y}\n" - "1: targets = {Y::func}\n"}, - // DeclRefExpr should know their using declaration. - {R"cpp( + "0: targets = {y}\n" + "1: targets = {Y::func}\n"}, + // DeclRefExpr should know their using declaration. + {R"cpp( namespace ns { void bar(int); } using ns::bar; @@ -857,9 +939,9 @@ TEST_F(FindExplicitReferencesTest, All) { $0^bar(10); } )cpp", - "0: targets = {bar}\n"}, - // References from a macro. - {R"cpp( + "0: targets = {bar}\n"}, + // References from a macro. + {R"cpp( #define FOO a #define BAR b @@ -867,10 +949,10 @@ TEST_F(FindExplicitReferencesTest, All) { $0^FOO+$1^BAR; } )cpp", - "0: targets = {a}\n" - "1: targets = {b}\n"}, - // No references from implicit nodes. - {R"cpp( + "0: targets = {a}\n" + "1: targets = {b}\n"}, + // No references from implicit nodes. + {R"cpp( struct vector { int *begin(); int *end(); @@ -882,15 +964,15 @@ TEST_F(FindExplicitReferencesTest, All) { } } )cpp", - "0: targets = {x}, decl\n" - "1: targets = {vector}\n" - "2: targets = {x}\n"}, + "0: targets = {x}, decl\n" + "1: targets = {vector}\n" + "2: targets = {x}\n"}, // Handle UnresolvedLookupExpr. // FIXME // This case fails when expensive checks are enabled. // Seems like the order of ns1::func and ns2::func isn't defined. #ifndef EXPENSIVE_CHECKS - {R"cpp( + {R"cpp( namespace ns1 { void func(char*); } namespace ns2 { void func(int*); } using namespace ns1; @@ -901,11 +983,11 @@ TEST_F(FindExplicitReferencesTest, All) { $0^func($1^t); } )cpp", - "0: targets = {ns1::func, ns2::func}\n" - "1: targets = {t}\n"}, + "0: targets = {ns1::func, ns2::func}\n" + "1: targets = {t}\n"}, #endif - // Handle UnresolvedMemberExpr. - {R"cpp( + // Handle UnresolvedMemberExpr. + {R"cpp( struct X { void func(char*); void func(int*); @@ -916,11 +998,11 @@ TEST_F(FindExplicitReferencesTest, All) { $0^x.$1^func($2^t); } )cpp", - "0: targets = {x}\n" - "1: targets = {X::func, X::func}\n" - "2: targets = {t}\n"}, - // Handle DependentScopeDeclRefExpr. - {R"cpp( + "0: targets = {x}\n" + "1: targets = {X::func, X::func}\n" + "2: targets = {t}\n"}, + // Handle DependentScopeDeclRefExpr. + {R"cpp( template struct S { static int value; @@ -931,11 +1013,11 @@ TEST_F(FindExplicitReferencesTest, All) { $0^S<$1^T>::$2^value; } )cpp", - "0: targets = {S}\n" - "1: targets = {T}\n" - "2: targets = {S::value}, qualifier = 'S::'\n"}, - // Handle CXXDependentScopeMemberExpr. - {R"cpp( + "0: targets = {S}\n" + "1: targets = {T}\n" + "2: targets = {S::value}, qualifier = 'S::'\n"}, + // Handle CXXDependentScopeMemberExpr. + {R"cpp( template struct S { int value; @@ -946,10 +1028,10 @@ TEST_F(FindExplicitReferencesTest, All) { $0^t.$1^value; } )cpp", - "0: targets = {t}\n" - "1: targets = {S::value}\n"}, - // Type template parameters. - {R"cpp( + "0: targets = {t}\n" + "1: targets = {S::value}\n"}, + // Type template parameters. + {R"cpp( template void foo() { static_cast<$0^T>(0); @@ -957,21 +1039,21 @@ TEST_F(FindExplicitReferencesTest, All) { $2^T $3^t; } )cpp", - "0: targets = {T}\n" - "1: targets = {T}\n" - "2: targets = {T}\n" - "3: targets = {t}, decl\n"}, - // Non-type template parameters. - {R"cpp( + "0: targets = {T}\n" + "1: targets = {T}\n" + "2: targets = {T}\n" + "3: targets = {t}, decl\n"}, + // Non-type template parameters. + {R"cpp( template void foo() { int $0^x = $1^I; } )cpp", - "0: targets = {x}, decl\n" - "1: targets = {I}\n"}, - // Template template parameters. - {R"cpp( + "0: targets = {x}, decl\n" + "1: targets = {I}\n"}, + // Template template parameters. + {R"cpp( template struct vector {}; template class TT, template class ...TP> @@ -982,16 +1064,16 @@ TEST_F(FindExplicitReferencesTest, All) { $6^foo<$7^TP...>(); } )cpp", - "0: targets = {TT}\n" - "1: targets = {x}, decl\n" - "2: targets = {foo}\n" - "3: targets = {TT}\n" - "4: targets = {foo}\n" - "5: targets = {vector}\n" - "6: targets = {foo}\n" - "7: targets = {TP}\n"}, - // Non-type template parameters with declarations. - {R"cpp( + "0: targets = {TT}\n" + "1: targets = {x}, decl\n" + "2: targets = {foo}\n" + "3: targets = {TT}\n" + "4: targets = {foo}\n" + "5: targets = {vector}\n" + "6: targets = {foo}\n" + "7: targets = {TP}\n"}, + // Non-type template parameters with declarations. + {R"cpp( int func(); template struct wrapper {}; @@ -1001,12 +1083,12 @@ TEST_F(FindExplicitReferencesTest, All) { $3^FuncParam(); } )cpp", - "0: targets = {wrapper<&func>}\n" - "1: targets = {func}\n" - "2: targets = {w}, decl\n" - "3: targets = {FuncParam}\n"}, - // declaration references. - {R"cpp( + "0: targets = {wrapper<&func>}\n" + "1: targets = {func}\n" + "2: targets = {w}, decl\n" + "3: targets = {FuncParam}\n"}, + // declaration references. + {R"cpp( namespace ns {} class S {}; void foo() { @@ -1018,19 +1100,19 @@ TEST_F(FindExplicitReferencesTest, All) { namespace $9^NS = $10^ns; } )cpp", - "0: targets = {Foo}, decl\n" - "1: targets = {foo()::Foo::Foo}, decl\n" - "2: targets = {Foo}\n" - "3: targets = {foo()::Foo::field}, decl\n" - "4: targets = {Var}, decl\n" - "5: targets = {E}, decl\n" - "6: targets = {foo()::ABC}, decl\n" - "7: targets = {INT}, decl\n" - "8: targets = {INT2}, decl\n" - "9: targets = {NS}, decl\n" - "10: targets = {ns}\n"}, - // User-defined conversion operator. - {R"cpp( + "0: targets = {Foo}, decl\n" + "1: targets = {foo()::Foo::Foo}, decl\n" + "2: targets = {Foo}\n" + "3: targets = {foo()::Foo::field}, decl\n" + "4: targets = {Var}, decl\n" + "5: targets = {E}, decl\n" + "6: targets = {foo()::ABC}, decl\n" + "7: targets = {INT}, decl\n" + "8: targets = {INT2}, decl\n" + "9: targets = {NS}, decl\n" + "10: targets = {ns}\n"}, + // User-defined conversion operator. + {R"cpp( void foo() { class $0^Bar {}; class $1^Foo { @@ -1043,18 +1125,18 @@ TEST_F(FindExplicitReferencesTest, All) { $7^f.$8^operator $9^Bar(); } )cpp", - "0: targets = {Bar}, decl\n" - "1: targets = {Foo}, decl\n" - "2: targets = {foo()::Foo::operator Bar}, decl\n" - "3: targets = {Bar}\n" - "4: targets = {Bar}\n" - "5: targets = {Foo}\n" - "6: targets = {f}, decl\n" - "7: targets = {f}\n" - "8: targets = {foo()::Foo::operator Bar}\n" - "9: targets = {Bar}\n"}, - // Destructor. - {R"cpp( + "0: targets = {Bar}, decl\n" + "1: targets = {Foo}, decl\n" + "2: targets = {foo()::Foo::operator Bar}, decl\n" + "3: targets = {Bar}\n" + "4: targets = {Bar}\n" + "5: targets = {Foo}\n" + "6: targets = {f}, decl\n" + "7: targets = {f}\n" + "8: targets = {foo()::Foo::operator Bar}\n" + "9: targets = {Bar}\n"}, + // Destructor. + {R"cpp( void foo() { class $0^Foo { public: @@ -1069,18 +1151,18 @@ TEST_F(FindExplicitReferencesTest, All) { $6^f.~ /*...*/ $7^Foo(); } )cpp", - "0: targets = {Foo}, decl\n" - // FIXME: It's better to target destructor's FunctionDecl instead of - // the type itself (similar to constructor). - "1: targets = {Foo}\n" - "2: targets = {foo()::Foo::destructMe}, decl\n" - "3: targets = {Foo}\n" - "4: targets = {Foo}\n" - "5: targets = {f}, decl\n" - "6: targets = {f}\n" - "7: targets = {Foo}\n"}, - // cxx constructor initializer. - {R"cpp( + "0: targets = {Foo}, decl\n" + // FIXME: It's better to target destructor's FunctionDecl instead of + // the type itself (similar to constructor). + "1: targets = {Foo}\n" + "2: targets = {foo()::Foo::destructMe}, decl\n" + "3: targets = {Foo}\n" + "4: targets = {Foo}\n" + "5: targets = {f}, decl\n" + "6: targets = {f}\n" + "7: targets = {Foo}\n"}, + // cxx constructor initializer. + {R"cpp( class Base {}; void foo() { // member initializer @@ -1100,34 +1182,34 @@ TEST_F(FindExplicitReferencesTest, All) { }; } )cpp", - "0: targets = {X}, decl\n" - "1: targets = {foo()::X::abc}, decl\n" - "2: targets = {foo()::X::X}, decl\n" - "3: targets = {foo()::X::abc}\n" - "4: targets = {Derived}, decl\n" - "5: targets = {Base}\n" - "6: targets = {Base}\n" - "7: targets = {foo()::Derived::B}, decl\n" - "8: targets = {foo()::Derived::Derived}, decl\n" - "9: targets = {Base}\n" - "10: targets = {Foo}, decl\n" - "11: targets = {foo()::Foo::Foo}, decl\n" - "12: targets = {foo()::Foo::Foo}, decl\n" - "13: targets = {Foo}\n"}, - // Anonymous entities should not be reported. - { - R"cpp( + "0: targets = {X}, decl\n" + "1: targets = {foo()::X::abc}, decl\n" + "2: targets = {foo()::X::X}, decl\n" + "3: targets = {foo()::X::abc}\n" + "4: targets = {Derived}, decl\n" + "5: targets = {Base}\n" + "6: targets = {Base}\n" + "7: targets = {foo()::Derived::B}, decl\n" + "8: targets = {foo()::Derived::Derived}, decl\n" + "9: targets = {Base}\n" + "10: targets = {Foo}, decl\n" + "11: targets = {foo()::Foo::Foo}, decl\n" + "12: targets = {foo()::Foo::Foo}, decl\n" + "13: targets = {Foo}\n"}, + // Anonymous entities should not be reported. + { + R"cpp( void foo() { class {} $0^x; int (*$1^fptr)(int $2^a, int) = nullptr; } )cpp", - "0: targets = {x}, decl\n" - "1: targets = {fptr}, decl\n" - "2: targets = {a}, decl\n"}, - // Namespace aliases should be handled properly. - { - R"cpp( + "0: targets = {x}, decl\n" + "1: targets = {fptr}, decl\n" + "2: targets = {a}, decl\n"}, + // Namespace aliases should be handled properly. + { + R"cpp( namespace ns { struct Type {}; } namespace alias = ns; namespace rec_alias = alias; @@ -1138,28 +1220,28 @@ TEST_F(FindExplicitReferencesTest, All) { $6^rec_alias::$7^Type $8^c; } )cpp", - "0: targets = {ns}\n" - "1: targets = {ns::Type}, qualifier = 'ns::'\n" - "2: targets = {a}, decl\n" - "3: targets = {alias}\n" - "4: targets = {ns::Type}, qualifier = 'alias::'\n" - "5: targets = {b}, decl\n" - "6: targets = {rec_alias}\n" - "7: targets = {ns::Type}, qualifier = 'rec_alias::'\n" - "8: targets = {c}, decl\n"}, - // Handle SizeOfPackExpr. - { - R"cpp( + "0: targets = {ns}\n" + "1: targets = {ns::Type}, qualifier = 'ns::'\n" + "2: targets = {a}, decl\n" + "3: targets = {alias}\n" + "4: targets = {ns::Type}, qualifier = 'alias::'\n" + "5: targets = {b}, decl\n" + "6: targets = {rec_alias}\n" + "7: targets = {ns::Type}, qualifier = 'rec_alias::'\n" + "8: targets = {c}, decl\n"}, + // Handle SizeOfPackExpr. + { + R"cpp( template void foo() { constexpr int $0^size = sizeof...($1^E); }; )cpp", - "0: targets = {size}, decl\n" - "1: targets = {E}\n"}, - // Class template argument deduction - { - R"cpp( + "0: targets = {size}, decl\n" + "1: targets = {E}\n"}, + // Class template argument deduction + { + R"cpp( template struct Test { Test(T); @@ -1168,51 +1250,51 @@ TEST_F(FindExplicitReferencesTest, All) { $0^Test $1^a(5); } )cpp", - "0: targets = {Test}\n" - "1: targets = {a}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {Test}\n" + "1: targets = {a}, decl\n"}, + // Templates + {R"cpp( namespace foo { template class $1^Bar {}; } )cpp", - "0: targets = {foo::Bar::T}, decl\n" - "1: targets = {foo::Bar}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {foo::Bar::T}, decl\n" + "1: targets = {foo::Bar}, decl\n"}, + // Templates + {R"cpp( namespace foo { template void $1^func(); } )cpp", - "0: targets = {T}, decl\n" - "1: targets = {foo::func}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {T}, decl\n" + "1: targets = {foo::func}, decl\n"}, + // Templates + {R"cpp( namespace foo { template $1^T $2^x; } )cpp", - "0: targets = {foo::T}, decl\n" - "1: targets = {foo::T}\n" - "2: targets = {foo::x}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {foo::T}, decl\n" + "1: targets = {foo::T}\n" + "2: targets = {foo::x}, decl\n"}, + // Templates + {R"cpp( template class vector {}; namespace foo { template using $1^V = $2^vector<$3^T>; } )cpp", - "0: targets = {foo::T}, decl\n" - "1: targets = {foo::V}, decl\n" - "2: targets = {vector}\n" - "3: targets = {foo::T}\n"}, - // Concept - { - R"cpp( + "0: targets = {foo::T}, decl\n" + "1: targets = {foo::V}, decl\n" + "2: targets = {vector}\n" + "3: targets = {foo::T}\n"}, + // Concept + { + R"cpp( template concept Drawable = requires (T t) { t.draw(); }; @@ -1223,17 +1305,17 @@ TEST_F(FindExplicitReferencesTest, All) { } } )cpp", - "0: targets = {T}, decl\n" - "1: targets = {Drawable}\n" - "2: targets = {T}\n" - "3: targets = {foo::bar}, decl\n" - "4: targets = {T}\n" - "5: targets = {t}, decl\n" - "6: targets = {t}\n" - "7: targets = {}\n"}, - // Objective-C: properties - { - R"cpp( + "0: targets = {T}, decl\n" + "1: targets = {Drawable}\n" + "2: targets = {T}\n" + "3: targets = {foo::bar}, decl\n" + "4: targets = {T}\n" + "5: targets = {t}, decl\n" + "6: targets = {t}\n" + "7: targets = {}\n"}, + // Objective-C: properties + { + R"cpp( @interface I {} @property(retain) I* x; @property(retain) I* y; @@ -1243,12 +1325,12 @@ TEST_F(FindExplicitReferencesTest, All) { $0^f.$1^x.$2^y = 0; } )cpp", - "0: targets = {f}\n" - "1: targets = {I::x}\n" - "2: targets = {I::y}\n"}, - // Objective-C: implicit properties - { - R"cpp( + "0: targets = {f}\n" + "1: targets = {I::x}\n" + "2: targets = {I::y}\n"}, + // Objective-C: implicit properties + { + R"cpp( @interface I {} -(I*)x; -(void)setY:(I*)y; @@ -1258,11 +1340,11 @@ TEST_F(FindExplicitReferencesTest, All) { $0^f.$1^x.$2^y = 0; } )cpp", - "0: targets = {f}\n" - "1: targets = {I::x}\n" - "2: targets = {I::setY:}\n"}, - // Designated initializers. - {R"cpp( + "0: targets = {f}\n" + "1: targets = {I::x}\n" + "2: targets = {I::setY:}\n"}, + // Designated initializers. + {R"cpp( void foo() { struct $0^Foo { int $1^Bar; @@ -1270,12 +1352,12 @@ TEST_F(FindExplicitReferencesTest, All) { $2^Foo $3^f { .$4^Bar = 42 }; } )cpp", - "0: targets = {Foo}, decl\n" - "1: targets = {foo()::Foo::Bar}, decl\n" - "2: targets = {Foo}\n" - "3: targets = {f}, decl\n" - "4: targets = {foo()::Foo::Bar}\n"}, - {R"cpp( + "0: targets = {Foo}, decl\n" + "1: targets = {foo()::Foo::Bar}, decl\n" + "2: targets = {Foo}\n" + "3: targets = {f}, decl\n" + "4: targets = {foo()::Foo::Bar}\n"}, + {R"cpp( void foo() { struct $0^Baz { int $1^Field; @@ -1295,7 +1377,7 @@ TEST_F(FindExplicitReferencesTest, All) { "6: targets = {bar}, decl\n" "7: targets = {foo()::Bar::Foo}\n" "8: targets = {foo()::Baz::Field}\n"}, - {R"cpp( + {R"cpp( template void crash(T); template @@ -1305,10 +1387,9 @@ TEST_F(FindExplicitReferencesTest, All) { )cpp", "0: targets = {crash}\n" "1: targets = {}\n" - "2: targets = {T}\n" - }, - // unknown template name should not crash. - {R"cpp( + "2: targets = {T}\n"}, + // unknown template name should not crash. + {R"cpp( template