diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 1e8cabbfb30..d1fac344735 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -405,6 +405,14 @@ struct ReferencedDeclInfo { IsSPI(SPI), ParentContexts(Parents) {} }; +struct LocationInfo { + StringRef Filename; + unsigned Offset = 0; + unsigned Length = 0; + unsigned Line = 0; + unsigned Column = 0; +}; + struct CursorSymbolInfo { UIdent Kind; UIdent DeclarationLang; @@ -430,11 +438,8 @@ struct CursorSymbolInfo { /// Non-empty if a generated interface editor document has previously been /// opened for the module the symbol came from. StringRef ModuleInterfaceName; - /// This is an (offset,length) pair. It is set only if the declaration has a - /// source location. - llvm::Optional> DeclarationLoc = None; - /// Set only if the declaration has a source location. - StringRef Filename; + /// Filename is non-empty if there's a source location. + LocationInfo Location; /// For methods this lists the USRs of the overrides in the class hierarchy. ArrayRef OverrideUSRs; /// Related declarations, overloaded functions etc., in annotated XML form. diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index 765df870200..bde5d26d096 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -2295,6 +2295,17 @@ ImmutableTextSnapshotRef SwiftEditorDocument::getLatestSnapshot() const { return Impl.EditableBuffer->getSnapshot(); } +std::pair +SwiftEditorDocument::getLineAndColumnInBuffer(unsigned Offset) { + llvm::sys::ScopedLock L(Impl.AccessMtx); + + auto SyntaxInfo = Impl.getSyntaxInfo(); + auto &SM = SyntaxInfo->getSourceManager(); + + auto Loc = SM.getLocForOffset(SyntaxInfo->getBufferID(), Offset); + return SM.getLineAndColumnInBuffer(Loc); +} + void SwiftEditorDocument::reportDocumentStructure(SourceFile &SrcFile, EditorConsumer &Consumer) { ide::SyntaxModelContext ModelContext(SrcFile); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index a3068db73ed..0a7898064f1 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -105,6 +105,7 @@ public: void removeCachedAST(); ImmutableTextSnapshotRef getLatestSnapshot() const; + std::pair getLineAndColumnInBuffer(unsigned Offset); void parse(ImmutableTextSnapshotRef Snapshot, SwiftLangSupport &Lang, bool BuildSyntaxTree, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 24d5d69f937..3af5776a4f5 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -600,38 +600,46 @@ mapOffsetToNewerSnapshot(unsigned Offset, return None; } -/// Tries to remap the location from a previous snapshot to the latest one. -static llvm::Optional> -tryRemappingLocToLatestSnapshot(SwiftLangSupport &Lang, - std::pair Range, - StringRef Filename, - ArrayRef PreviousASTSnaps) { - ImmutableTextSnapshotRef LatestSnap; - if (auto EditorDoc = Lang.getEditorDocuments()->findByPath(Filename)) - LatestSnap = EditorDoc->getLatestSnapshot(); +/// Tries to remap the location from a previous snapshot to the latest one and +/// then sets the location's line and column. +static void mapLocToLatestSnapshot( + SwiftLangSupport &Lang, LocationInfo &Location, + ArrayRef PreviousASTSnaps) { + auto EditorDoc = Lang.getEditorDocuments()->findByPath(Location.Filename); + if (!EditorDoc) + return; + + ImmutableTextSnapshotRef LatestSnap = EditorDoc->getLatestSnapshot(); if (!LatestSnap) - return Range; + return; for (auto &PrevSnap : PreviousASTSnaps) { if (PrevSnap->isFromSameBuffer(LatestSnap)) { if (PrevSnap->getStamp() == LatestSnap->getStamp()) - return Range; + break; - auto OptBegin = mapOffsetToNewerSnapshot(Range.first, + auto OptBegin = mapOffsetToNewerSnapshot(Location.Offset, PrevSnap, LatestSnap); - if (!OptBegin.hasValue()) - return None; + if (!OptBegin.hasValue()) { + Location.Filename = StringRef(); + return; + } - auto OptEnd = mapOffsetToNewerSnapshot(Range.first+Range.second, + auto OptEnd = mapOffsetToNewerSnapshot(Location.Offset + + Location.Length, PrevSnap, LatestSnap); - if (!OptEnd.hasValue()) - return None; + if (!OptEnd.hasValue()) { + Location.Filename = StringRef(); + return; + } - return std::make_pair(*OptBegin, *OptEnd-*OptBegin); + Location.Offset = *OptBegin; + Location.Length = *OptEnd - *OptBegin; } } - return Range; + std::tie(Location.Line, Location.Column) = + EditorDoc->getLineAndColumnInBuffer(Location.Offset); } @@ -802,10 +810,9 @@ static ArrayRef copyAndClearArray(llvm::BumpPtrAllocator &Allocator, return Ref; } -static void getLocationInfoForClangNode( - ClangNode ClangNode, ClangImporter *Importer, - llvm::Optional> &DeclarationLoc, - StringRef &Filename) { +static void setLocationInfoForClangNode(ClangNode ClangNode, + ClangImporter *Importer, + LocationInfo &Location) { clang::ASTContext &ClangCtx = Importer->getClangASTContext(); clang::SourceManager &ClangSM = ClangCtx.getSourceManager(); @@ -826,12 +833,15 @@ static void getLocationInfoForClangNode( ClangSM.getDecomposedLoc(CharRange.getBegin()); if (!Decomp.first.isInvalid()) { if (auto FE = ClangSM.getFileEntryForID(Decomp.first)) { - Filename = FE->getName(); + Location.Filename = FE->getName(); std::pair EndDecomp = ClangSM.getDecomposedLoc(CharRange.getEnd()); - DeclarationLoc = {Decomp.second, EndDecomp.second - Decomp.second}; + Location.Offset = Decomp.second; + Location.Length = EndDecomp.second-Decomp.second; + Location.Line = ClangSM.getLineNumber(Decomp.first, Decomp.second); + Location.Column = ClangSM.getColumnNumber(Decomp.first, Decomp.second); } } } @@ -841,10 +851,8 @@ static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) { return SM.getByteDistance(TokenRange.Start, CharEndLoc); } -static void -getLocationInfo(const ValueDecl *VD, - llvm::Optional> &DeclarationLoc, - StringRef &Filename) { +static void setLocationInfo(const ValueDecl *VD, + LocationInfo &Location) { ASTContext &Ctx = VD->getASTContext(); SourceManager &SM = Ctx.SourceMgr; @@ -870,14 +878,15 @@ getLocationInfo(const ValueDecl *VD, } unsigned DeclBufID = SM.findBufferContainingLoc(Loc); - DeclarationLoc = {SM.getLocOffsetInBuffer(Loc, DeclBufID), - NameLen}; - Filename = SM.getIdentifierForBuffer(DeclBufID); + Location.Filename = SM.getIdentifierForBuffer(DeclBufID); + Location.Offset = SM.getLocOffsetInBuffer(Loc, DeclBufID); + Location.Length = NameLen; + std::tie(Location.Line, Location.Column) = SM.getLineAndColumnInBuffer( + Loc, DeclBufID); } else if (ClangNode) { ClangImporter *Importer = - static_cast(Ctx.getClangModuleLoader()); - return getLocationInfoForClangNode(ClangNode, Importer, DeclarationLoc, - Filename); + static_cast(Ctx.getClangModuleLoader()); + setLocationInfoForClangNode(ClangNode, Importer, Location); } } @@ -1011,12 +1020,10 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo, Lang.getIFaceGenContexts().find(Symbol.ModuleName, Invoc)) Symbol.ModuleInterfaceName = IFaceGenRef->getDocumentName(); - getLocationInfo(DInfo.OriginalProperty, Symbol.DeclarationLoc, - Symbol.Filename); - if (Symbol.DeclarationLoc.hasValue()) { - Symbol.DeclarationLoc = tryRemappingLocToLatestSnapshot( - Lang, *Symbol.DeclarationLoc, Symbol.Filename, PreviousSnaps); - if (!Symbol.DeclarationLoc.hasValue()) { + setLocationInfo(DInfo.OriginalProperty, Symbol.Location); + if (!Symbol.Location.Filename.empty()) { + mapLocToLatestSnapshot(Lang, Symbol.Location, PreviousSnaps); + if (Symbol.Location.Filename.empty()) { return llvm::createStringError( llvm::inconvertibleErrorCode(), "Failed to remap declaration to latest snapshot."); diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index d5d3b52692b..ec91a1a9a51 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -1609,9 +1609,11 @@ struct ResponseSymbolInfo { const char *SymbolGraph = nullptr; const char *ModuleName = nullptr; const char *ModuleInterfaceName = nullptr; - llvm::Optional Offset; - unsigned Length = 0; const char *FilePath = nullptr; + unsigned Offset = 0; + unsigned Length = 0; + unsigned Line = 0; + unsigned Column = 0; std::vector OverrideUSRs; std::vector AnnotatedRelatedDeclarations; std::vector ModuleGroups; @@ -1665,15 +1667,14 @@ struct ResponseSymbolInfo { Symbol.ModuleInterfaceName = sourcekitd_variant_dictionary_get_string(Info, KeyModuleInterfaceName); - sourcekitd_variant_t OffsetObj = - sourcekitd_variant_dictionary_get_value(Info, KeyOffset); - if (sourcekitd_variant_get_type(OffsetObj) != - SOURCEKITD_VARIANT_TYPE_NULL) { - Symbol.Offset = sourcekitd_variant_int64_get_value(OffsetObj); - Symbol.Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength); - } Symbol.FilePath = sourcekitd_variant_dictionary_get_string(Info, KeyFilePath); + if (Symbol.FilePath) { + Symbol.Offset = sourcekitd_variant_dictionary_get_int64(Info, KeyOffset); + Symbol.Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength); + Symbol.Line = sourcekitd_variant_dictionary_get_int64(Info, KeyLine); + Symbol.Column = sourcekitd_variant_dictionary_get_int64(Info, KeyColumn); + } Symbol.OverrideUSRs = readStringArray(Info, KeyOverrides, KeyUSR); Symbol.AnnotatedRelatedDeclarations = @@ -1728,14 +1729,19 @@ struct ResponseSymbolInfo { } OS << Kind << " ("; - if (Offset.hasValue()) { + if (FilePath) { if (CurrentFilename != StringRef(FilePath)) - OS << FilePath << ":"; - auto LineCol = resolveToLineCol(Offset.getValue(), FilePath, VFSFiles); - OS << LineCol.first << ':' << LineCol.second; - auto EndLineCol = - resolveToLineCol(Offset.getValue() + Length, FilePath, VFSFiles); - OS << '-' << EndLineCol.first << ':' << EndLineCol.second; + OS << FilePath << ':'; + + auto LineCol = resolveToLineCol(Offset, FilePath, VFSFiles); + if (LineCol.first != Line || LineCol.second != Column) { + OS << "*offset does not match line/column in response*"; + } else { + OS << LineCol.first << ':' << LineCol.second; + auto EndLineCol = resolveToLineCol(Offset + Length, FilePath, + VFSFiles); + OS << '-' << EndLineCol.first << ':' << EndLineCol.second; + } } OS << ")" << '\n'; diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 7fc21d8cd28..2892e521304 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -1764,11 +1764,12 @@ static void addCursorSymbolInfo(const CursorSymbolInfo &Symbol, Elem.set(KeyModuleName, Symbol.ModuleName); if (!Symbol.ModuleInterfaceName.empty()) Elem.set(KeyModuleInterfaceName, Symbol.ModuleInterfaceName); - if (Symbol.DeclarationLoc.hasValue()) { - Elem.set(KeyOffset, Symbol.DeclarationLoc.getValue().first); - Elem.set(KeyLength, Symbol.DeclarationLoc.getValue().second); - if (!Symbol.Filename.empty()) - Elem.set(KeyFilePath, Symbol.Filename); + if (!Symbol.Location.Filename.empty()) { + Elem.set(KeyFilePath, Symbol.Location.Filename); + Elem.set(KeyOffset, Symbol.Location.Offset); + Elem.set(KeyLength, Symbol.Location.Length); + Elem.set(KeyLine, Symbol.Location.Line); + Elem.set(KeyColumn, Symbol.Location.Column); } if (!Symbol.OverrideUSRs.empty()) { diff --git a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp index 2d518137d72..36ac4348bba 100644 --- a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp +++ b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp @@ -96,7 +96,8 @@ struct TestCursorInfo { std::string Name; std::string Typename; std::string Filename; - Optional> DeclarationLoc; + unsigned Offset; + unsigned Length; }; class CursorInfoTest : public ::testing::Test { @@ -162,8 +163,9 @@ public: const CursorSymbolInfo &MainSymbol = Info.Symbols[0]; TestInfo.Name = std::string(MainSymbol.Name.str()); TestInfo.Typename = MainSymbol.TypeName.str(); - TestInfo.Filename = MainSymbol.Filename.str(); - TestInfo.DeclarationLoc = MainSymbol.DeclarationLoc; + TestInfo.Filename = MainSymbol.Location.Filename.str(); + TestInfo.Offset = MainSymbol.Location.Offset; + TestInfo.Length = MainSymbol.Location.Length; } sema.signal(); }); @@ -223,9 +225,8 @@ TEST_F(CursorInfoTest, EditAfter) { EXPECT_STREQ("foo", Info.Name.c_str()); EXPECT_STREQ("Int", Info.Typename.c_str()); EXPECT_STREQ(DocName, Info.Filename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("foo"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("foo"), Info.Length); StringRef TextToReplace = "0"; replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(), @@ -239,9 +240,8 @@ TEST_F(CursorInfoTest, EditAfter) { EXPECT_STREQ("foo", Info.Name.c_str()); EXPECT_STREQ("Int", Info.Typename.c_str()); EXPECT_STREQ(DocName, Info.Filename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("foo"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("foo"), Info.Length); } TEST_F(CursorInfoTest, EditBefore) { @@ -258,9 +258,8 @@ TEST_F(CursorInfoTest, EditBefore) { EXPECT_STREQ("foo", Info.Name.c_str()); EXPECT_STREQ("Int", Info.Typename.c_str()); EXPECT_STREQ(DocName, Info.Filename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("foo"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("foo"), Info.Length); StringRef TextToReplace = "0"; replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(), @@ -276,9 +275,8 @@ TEST_F(CursorInfoTest, EditBefore) { EXPECT_STREQ("foo", Info.Name.c_str()); EXPECT_STREQ("Int", Info.Typename.c_str()); EXPECT_STREQ(DocName, Info.Filename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("foo"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("foo"), Info.Length); } TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) { @@ -306,9 +304,8 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) { Info = getCursor(DocName, FooRefOffs, Args); EXPECT_STREQ("foo", Info.Name.c_str()); EXPECT_STREQ("[Int : Int]", Info.Typename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("foo"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("foo"), Info.Length); } TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) { @@ -336,9 +333,8 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) { Info = getCursor(DocName, FooRefOffs, Args); EXPECT_STREQ("foo", Info.Name.c_str()); EXPECT_STREQ("[Int : Int]", Info.Typename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("foo"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("foo"), Info.Length); } TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { @@ -367,9 +363,8 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) { Info = getCursor(DocName, FooRefOffs, Args); EXPECT_STREQ("fog", Info.Name.c_str()); EXPECT_STREQ("[Int : Int]", Info.Typename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("fog"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("fog"), Info.Length); } TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) { @@ -396,7 +391,6 @@ TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) { auto Info = getCursor(DocName, FooRefOffs, Args); EXPECT_STREQ("fog", Info.Name.c_str()); EXPECT_STREQ("Int", Info.Typename.c_str()); - ASSERT_TRUE(Info.DeclarationLoc.hasValue()); - EXPECT_EQ(FooOffs, Info.DeclarationLoc->first); - EXPECT_EQ(strlen("fog"), Info.DeclarationLoc->second); + EXPECT_EQ(FooOffs, Info.Offset); + EXPECT_EQ(strlen("fog"), Info.Length); }