malaperle updated this revision to Diff 129292.
malaperle added a comment.
Store map in PremableData and copy it on reparse.
Convert SourceLoc to Position when comparing with include Positions.
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D38639
Files:
clangd/ClangdUnit.cpp
clangd/ClangdUnit.h
clangd/Protocol.h
clangd/SourceCode.cpp
clangd/SourceCode.h
clangd/XRefs.cpp
unittests/clangd/ClangdTests.cpp
Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- unittests/clangd/ClangdTests.cpp
+++ unittests/clangd/ClangdTests.cpp
@@ -751,6 +751,74 @@
EXPECT_FALSE(PathResult.hasValue());
}
+TEST_F(ClangdVFSTest, CheckDefinitionIncludes) {
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB;
+
+ ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+ /*StorePreamblesInMemory=*/true);
+
+ auto FooCpp = getVirtualTestFilePath("foo.cpp");
+ const auto SourceContents = R"cpp(
+ #include "foo.h"
+ #include "invalid.h"
+ int b = a;
+ // test
+ int foo;
+ #include "foo.h"
+ )cpp";
+ FS.Files[FooCpp] = SourceContents;
+ auto FooH = getVirtualTestFilePath("foo.h");
+ const auto HeaderContents = "int a;";
+
+ FS.Files[FooCpp] = SourceContents;
+ FS.Files[FooH] = HeaderContents;
+
+ Server.addDocument(Context::empty(), FooH, HeaderContents);
+ Server.addDocument(Context::empty(), FooCpp, SourceContents);
+
+ Position P = Position{1, 11};
+
+ auto ExpectedLocations = Server.findDefinitions(Context::empty(), FooCpp, P);
+ ASSERT_TRUE(!!ExpectedLocations);
+ std::vector<Location> Locations = ExpectedLocations->Value;
+ EXPECT_TRUE(!Locations.empty());
+ std::string s("file:///");
+ std::string check = Locations[0].uri.uri;
+ check = check.erase(0, s.size() - 1);
+ check = check.substr(0, check.size());
+ ASSERT_EQ(check, FooH);
+ ASSERT_EQ(Locations[0].range.start.line, 0);
+ ASSERT_EQ(Locations[0].range.start.character, 0);
+ ASSERT_EQ(Locations[0].range.end.line, 0);
+ ASSERT_EQ(Locations[0].range.end.character, 0);
+
+ // Test include in preamble
+ Position P2 = Position{1, 11};
+
+ ExpectedLocations = Server.findDefinitions(Context::empty(), FooCpp, P2);
+ ASSERT_TRUE(!!ExpectedLocations);
+ Locations = ExpectedLocations->Value;
+ EXPECT_TRUE(!Locations.empty());
+
+ // Test invalid include
+ Position P3 = Position{2, 11};
+
+ ExpectedLocations = Server.findDefinitions(Context::empty(), FooCpp, P3);
+ ASSERT_TRUE(!!ExpectedLocations);
+ Locations = ExpectedLocations->Value;
+ EXPECT_TRUE(Locations.empty());
+
+ // Test include outside of Preamble
+ Position P4 = Position{6, 11};
+
+ ExpectedLocations = Server.findDefinitions(Context::empty(), FooCpp, P4);
+ ASSERT_TRUE(!!ExpectedLocations);
+ Locations = ExpectedLocations->Value;
+ EXPECT_TRUE(!Locations.empty());
+}
+
TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) {
class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer {
public:
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -7,6 +7,7 @@
//
//===---------------------------------------------------------------------===//
#include "XRefs.h"
+#include "SourceCode.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexingAction.h"
namespace clang {
@@ -103,12 +104,8 @@
return llvm::None;
SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
SourceMgr, LangOpts);
- Position Begin;
- Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
- Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
- Position End;
- End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
- End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+ Position Begin = sourceLocToPosition(SourceMgr, LocStart);
+ Position End = sourceLocToPosition(SourceMgr, LocEnd);
Range R = {Begin, End};
Location L;
@@ -142,6 +139,21 @@
indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
DeclMacrosFinder, IndexOpts);
+ /// Process targets for paths inside #include directive.
+ std::vector<Location> IncludeTargets;
+ for (auto &IncludeLoc : AST.getIRM()) {
+ Range R = IncludeLoc.first;
+ const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+ Position Pos = sourceLocToPosition(SourceMgr, SourceLocationBeg);
+
+ if (R.start.line == Pos.line && R.start.character <= Pos.character &&
+ Pos.character <= R.end.character) {
+ IncludeTargets.push_back(Location{URI::fromFile(IncludeLoc.second),
+ Range{Position{0, 0}, Position{0, 0}}});
+ return IncludeTargets;
+ }
+ }
+
std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls();
std::vector<const MacroInfo *> MacroInfos =
DeclMacrosFinder->takeMacroInfos();
@@ -217,13 +229,8 @@
DocumentHighlight getDocumentHighlight(SourceRange SR,
DocumentHighlightKind Kind) {
const SourceManager &SourceMgr = AST.getSourceManager();
- SourceLocation LocStart = SR.getBegin();
- Position Begin;
- Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
- Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
- Position End;
- End.line = SourceMgr.getSpellingLineNumber(SR.getEnd()) - 1;
- End.character = SourceMgr.getSpellingColumnNumber(SR.getEnd()) - 1;
+ Position Begin = sourceLocToPosition(SourceMgr, SR.getBegin());
+ Position End = sourceLocToPosition(SourceMgr, SR.getEnd());
Range R = {Begin, End};
DocumentHighlight DH;
DH.range = R;
Index: clangd/SourceCode.h
===================================================================
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -14,16 +14,22 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H
#include "Protocol.h"
+#include "clang/Basic/SourceLocation.h"
namespace clang {
+class SourceManager;
+
namespace clangd {
/// Turn a [line, column] pair into an offset in Code.
size_t positionToOffset(llvm::StringRef Code, Position P);
/// Turn an offset in Code into a [line, column] pair.
Position offsetToPosition(llvm::StringRef Code, size_t Offset);
+/// Turn a SourceLocation into a [line, column] pair.
+Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc);
+
} // namespace clangd
} // namespace clang
#endif
Index: clangd/SourceCode.cpp
===================================================================
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -8,6 +8,8 @@
//===----------------------------------------------------------------------===//
#include "SourceCode.h"
+#include "clang/Basic/SourceManager.h"
+
namespace clang {
namespace clangd {
using namespace llvm;
@@ -36,6 +38,10 @@
return {Lines, static_cast<int>(Offset - StartOfLine)};
}
+Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc) {
+ return {static_cast<int>(SM.getSpellingLineNumber(Loc)) - 1,
+ static_cast<int>(SM.getSpellingColumnNumber(Loc)) - 1};
+}
+
} // namespace clangd
} // namespace clang
-
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -112,6 +112,14 @@
};
bool fromJSON(const json::Expr &, Range &);
json::Expr toJSON(const Range &);
+
+class RangeHash {
+public:
+ std::size_t operator()(const Range &R) const {
+ return ((R.start.line & 0x18) << 3) | ((R.start.character & 0x18) << 1) |
+ ((R.end.line & 0x18) >> 1) | ((R.end.character & 0x18) >> 3);
+ }
+};
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Range &);
struct Location {
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -23,6 +23,7 @@
#include <future>
#include <memory>
#include <mutex>
+#include <unordered_map>
namespace llvm {
class raw_ostream;
@@ -47,15 +48,18 @@
llvm::SmallVector<TextEdit, 1> FixIts;
};
+using IncludeReferenceMap = std::unordered_map<Range, Path, RangeHash>;
+
// Stores Preamble and associated data.
struct PreambleData {
PreambleData(PrecompiledPreamble Preamble,
std::vector<serialization::DeclID> TopLevelDeclIDs,
- std::vector<DiagWithFixIts> Diags);
+ std::vector<DiagWithFixIts> Diags, IncludeReferenceMap IRM);
PrecompiledPreamble Preamble;
std::vector<serialization::DeclID> TopLevelDeclIDs;
std::vector<DiagWithFixIts> Diags;
+ IncludeReferenceMap IRM;
};
/// Stores and provides access to parsed AST.
@@ -89,12 +93,14 @@
const std::vector<DiagWithFixIts> &getDiagnostics() const;
+ const IncludeReferenceMap &getIRM() const { return IRM; };
+
private:
ParsedAST(std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
std::vector<const Decl *> TopLevelDecls,
- std::vector<DiagWithFixIts> Diags);
+ std::vector<DiagWithFixIts> Diags, IncludeReferenceMap IRM);
private:
void ensurePreambleDeclsDeserialized();
@@ -114,6 +120,7 @@
std::vector<DiagWithFixIts> Diags;
std::vector<const Decl *> TopLevelDecls;
bool PreambleDeclsDeserialized;
+ IncludeReferenceMap IRM;
};
// Provides thread-safe access to ParsedAST.
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -71,12 +71,53 @@
std::vector<const Decl *> TopLevelDecls;
};
+// Converts a half-open clang source range to an LSP range.
+// Note that clang also uses closed source ranges, which this can't handle!
+Range toRange(CharSourceRange R, const SourceManager &M) {
+ // Clang is 1-based, LSP uses 0-based indexes.
+ return {{static_cast<int>(M.getSpellingLineNumber(R.getBegin())) - 1,
+ static_cast<int>(M.getSpellingColumnNumber(R.getBegin())) - 1},
+ {static_cast<int>(M.getSpellingLineNumber(R.getEnd())) - 1,
+ static_cast<int>(M.getSpellingColumnNumber(R.getEnd())) - 1}};
+}
+
+class IncludeRefsCollector : public PPCallbacks {
+public:
+ IncludeRefsCollector(SourceManager &SourceMgr, IncludeReferenceMap &IRM)
+ : SourceMgr(SourceMgr), IRM(IRM) {}
+
+ void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+ StringRef FileName, bool IsAngled,
+ CharSourceRange FilenameRange, const FileEntry *File,
+ StringRef SearchPath, StringRef RelativePath,
+ const Module *Imported) override {
+ auto SR = FilenameRange.getAsRange();
+ if (SR.isInvalid() || !File || File->tryGetRealPathName().empty())
+ return;
+
+ if (SourceMgr.isInMainFile(FilenameRange.getAsRange().getBegin())) {
+ // Only inclusion directives in the main file make sense. The user cannot
+ // select directives not in the main file.
+ IRM.insert(
+ {toRange(FilenameRange, SourceMgr), File->tryGetRealPathName()});
+ }
+ }
+
+private:
+ SourceManager &SourceMgr;
+ IncludeReferenceMap &IRM;
+};
+
class CppFilePreambleCallbacks : public PreambleCallbacks {
public:
std::vector<serialization::DeclID> takeTopLevelDeclIDs() {
return std::move(TopLevelDeclIDs);
}
+ IncludeReferenceMap takeIncludeReferenceMap() { return std::move(IRM); }
+
+ CppFilePreambleCallbacks() : SourceMgr(nullptr) {}
+
void AfterPCHEmitted(ASTWriter &Writer) override {
TopLevelDeclIDs.reserve(TopLevelDecls.size());
for (Decl *D : TopLevelDecls) {
@@ -95,9 +136,20 @@
}
}
+ void BeforeExecute(CompilerInstance &CI) override {
+ SourceMgr = &CI.getSourceManager();
+ }
+
+ std::unique_ptr<PPCallbacks> createPPCallbacks() override {
+ assert(SourceMgr && "SourceMgr must be set at this point");
+ return llvm::make_unique<IncludeRefsCollector>(*SourceMgr, IRM);
+ }
+
private:
std::vector<Decl *> TopLevelDecls;
std::vector<serialization::DeclID> TopLevelDeclIDs;
+ SourceManager *SourceMgr;
+ IncludeReferenceMap IRM;
};
/// Convert from clang diagnostic level to LSP severity.
@@ -129,16 +181,6 @@
return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
}
-// Converts a half-open clang source range to an LSP range.
-// Note that clang also uses closed source ranges, which this can't handle!
-Range toRange(CharSourceRange R, const SourceManager &M) {
- // Clang is 1-based, LSP uses 0-based indexes.
- return {{static_cast<int>(M.getSpellingLineNumber(R.getBegin())) - 1,
- static_cast<int>(M.getSpellingColumnNumber(R.getBegin())) - 1},
- {static_cast<int>(M.getSpellingLineNumber(R.getEnd())) - 1,
- static_cast<int>(M.getSpellingColumnNumber(R.getEnd())) - 1}};
-}
-
// Clang diags have a location (shown as ^) and 0 or more ranges (~~~~).
// LSP needs a single range.
Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
@@ -234,6 +276,11 @@
std::vector<DiagWithFixIts> ASTDiags;
StoreDiagsConsumer UnitDiagsConsumer(/*ref*/ ASTDiags);
+ IncludeReferenceMap IRM;
+ // Copy over the includes from the preamble, then combine with the
+ // non-preamble includes below.
+ if (Preamble)
+ IRM = Preamble->IRM;
const PrecompiledPreamble *PreamblePCH =
Preamble ? &Preamble->Preamble : nullptr;
@@ -252,6 +299,10 @@
MainInput.getFile());
return llvm::None;
}
+
+ Clang->getPreprocessor().addPPCallbacks(
+ llvm::make_unique<IncludeRefsCollector>(Clang->getSourceManager(), IRM));
+
if (!Action->Execute())
log(Ctx, "Execute() failed when building AST for " + MainInput.getFile());
@@ -261,7 +312,7 @@
std::vector<const Decl *> ParsedDecls = Action->takeTopLevelDecls();
return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
- std::move(ParsedDecls), std::move(ASTDiags));
+ std::move(ParsedDecls), std::move(ASTDiags), std::move(IRM));
}
namespace {
@@ -335,19 +386,21 @@
PreambleData::PreambleData(PrecompiledPreamble Preamble,
std::vector<serialization::DeclID> TopLevelDeclIDs,
- std::vector<DiagWithFixIts> Diags)
+ std::vector<DiagWithFixIts> Diags,
+ IncludeReferenceMap IRM)
: Preamble(std::move(Preamble)),
- TopLevelDeclIDs(std::move(TopLevelDeclIDs)), Diags(std::move(Diags)) {}
+ TopLevelDeclIDs(std::move(TopLevelDeclIDs)), Diags(std::move(Diags)),
+ IRM(std::move(IRM)) {}
ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
std::vector<const Decl *> TopLevelDecls,
- std::vector<DiagWithFixIts> Diags)
+ std::vector<DiagWithFixIts> Diags, IncludeReferenceMap IRM)
: Preamble(std::move(Preamble)), Clang(std::move(Clang)),
Action(std::move(Action)), Diags(std::move(Diags)),
- TopLevelDecls(std::move(TopLevelDecls)),
- PreambleDeclsDeserialized(false) {
+ TopLevelDecls(std::move(TopLevelDecls)), PreambleDeclsDeserialized(false),
+ IRM(std::move(IRM)) {
assert(this->Clang);
assert(this->Action);
}
@@ -556,7 +609,8 @@
return std::make_shared<PreambleData>(
std::move(*BuiltPreamble),
SerializedDeclsCollector.takeTopLevelDeclIDs(),
- std::move(PreambleDiags));
+ std::move(PreambleDiags),
+ SerializedDeclsCollector.takeIncludeReferenceMap());
} else {
log(Ctx,
"Could not build a preamble for file " + Twine(That->FileName));
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits