ilya-biryukov updated this revision to Diff 192673. ilya-biryukov marked 4 inline comments as done. ilya-biryukov added a comment.
- Move Range into the body of Annotations - Use triple-slash comments - Added a FIXME that we might want to change the syntax - Move the doc comment to the class Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D59814/new/ https://reviews.llvm.org/D59814 Files: clang-tools-extra/unittests/clangd/Annotations.cpp clang-tools-extra/unittests/clangd/Annotations.h clang/unittests/Sema/CMakeLists.txt clang/unittests/Sema/CodeCompleteTest.cpp llvm/include/llvm/Testing/Support/Annotations.h llvm/lib/Testing/Support/Annotations.cpp llvm/lib/Testing/Support/CMakeLists.txt
Index: llvm/lib/Testing/Support/CMakeLists.txt =================================================================== --- llvm/lib/Testing/Support/CMakeLists.txt +++ llvm/lib/Testing/Support/CMakeLists.txt @@ -2,6 +2,7 @@ add_definitions(-DGTEST_HAS_TR1_TUPLE=0) add_llvm_library(LLVMTestingSupport + Annotations.cpp Error.cpp SupportHelpers.cpp Index: llvm/lib/Testing/Support/Annotations.cpp =================================================================== --- llvm/lib/Testing/Support/Annotations.cpp +++ llvm/lib/Testing/Support/Annotations.cpp @@ -6,11 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "Annotations.h" -#include "SourceCode.h" +#include "llvm/Testing/Support/Annotations.h" -namespace clang { -namespace clangd { +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; // Crash if the assertion fails, printing the message and testcase. // More elegant error handling isn't needed for unit tests. @@ -22,30 +23,31 @@ } Annotations::Annotations(llvm::StringRef Text) { - auto Here = [this] { return offsetToPosition(Code, Code.size()); }; auto Require = [Text](bool Assertion, const char *Msg) { require(Assertion, Msg, Text); }; llvm::Optional<llvm::StringRef> Name; - llvm::SmallVector<std::pair<llvm::StringRef, Position>, 8> OpenRanges; + llvm::SmallVector<std::pair<llvm::StringRef, size_t>, 8> OpenRanges; Code.reserve(Text.size()); while (!Text.empty()) { if (Text.consume_front("^")) { - Points[Name.getValueOr("")].push_back(Here()); - Name = None; + Points[Name.getValueOr("")].push_back(Code.size()); + Name = llvm::None; continue; } if (Text.consume_front("[[")) { - OpenRanges.emplace_back(Name.getValueOr(""), Here()); - Name = None; + OpenRanges.emplace_back(Name.getValueOr(""), Code.size()); + Name = llvm::None; continue; } Require(!Name, "$name should be followed by ^ or [["); if (Text.consume_front("]]")) { Require(!OpenRanges.empty(), "unmatched ]]"); - Ranges[OpenRanges.back().first].push_back( - {OpenRanges.back().second, Here()}); + Range R; + R.Begin = OpenRanges.back().second; + R.End = Code.size(); + Ranges[OpenRanges.back().first].push_back(R); OpenRanges.pop_back(); continue; } @@ -61,26 +63,27 @@ Require(OpenRanges.empty(), "unmatched [["); } -Position Annotations::point(llvm::StringRef Name) const { +size_t Annotations::point(llvm::StringRef Name) const { auto I = Points.find(Name); require(I != Points.end() && I->getValue().size() == 1, "expected exactly one point", Code); return I->getValue()[0]; } -std::vector<Position> Annotations::points(llvm::StringRef Name) const { + +std::vector<size_t> Annotations::points(llvm::StringRef Name) const { auto P = Points.lookup(Name); return {P.begin(), P.end()}; } -Range Annotations::range(llvm::StringRef Name) const { + +Annotations::Range Annotations::range(llvm::StringRef Name) const { auto I = Ranges.find(Name); require(I != Ranges.end() && I->getValue().size() == 1, "expected exactly one range", Code); return I->getValue()[0]; } -std::vector<Range> Annotations::ranges(llvm::StringRef Name) const { + +std::vector<Annotations::Range> +Annotations::ranges(llvm::StringRef Name) const { auto R = Ranges.lookup(Name); return {R.begin(), R.end()}; } - -} // namespace clangd -} // namespace clang Index: llvm/include/llvm/Testing/Support/Annotations.h =================================================================== --- /dev/null +++ llvm/include/llvm/Testing/Support/Annotations.h @@ -0,0 +1,81 @@ +//===--- Annotations.h - Annotated source code for tests ---------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TESTING_SUPPORT_ANNOTATIONS_H +#define LLVM_TESTING_SUPPORT_ANNOTATIONS_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include <vector> + +namespace llvm { + +/// Annotations lets you mark points and ranges inside source code, for tests: +/// +/// Annotations Example(R"cpp( +/// int complete() { x.pri^ } // ^ indicates a point +/// void err() { [["hello" == 42]]; } // [[this is a range]] +/// $definition^class Foo{}; // points can be named: "definition" +/// $fail[[static_assert(false, "")]] // ranges can be named too: "fail" +/// )cpp"); +/// +/// StringRef Code = Example.code(); // annotations stripped. +/// std::vector<size_t> PP = Example.points(); // all unnamed points +/// size_t P = Example.point(); // there must be exactly one +/// llvm::Range R = Example.range("fail"); // find named ranges +/// +/// Points/ranges are coordinated into `code()` which is stripped of +/// annotations. +/// +/// Ranges may be nested (and points can be inside ranges), but there's no way +/// to define general overlapping ranges. +/// +/// FIXME: the choice of the marking syntax makes it impossible to represent +/// some of the C++ and Objective C constructs (including common ones +/// like C++ attributes). We can fix this by: +/// 1. introducing an escaping mechanism for the special characters, +/// 2. making characters for marking points and ranges configurable, +/// 3. changing the syntax to something less commonly used, +/// 4. ... +class Annotations { +public: + /// Two offsets pointing to a continuous substring. End is not included, i.e. + /// represents a half-open range. + struct Range { + size_t Begin = 0; + size_t End = 0; + }; + + /// Parses the annotations from Text. Crashes if it's malformed. + Annotations(llvm::StringRef Text); + + /// The input text with all annotations stripped. + /// All points and ranges are relative to this stripped text. + llvm::StringRef code() const { return Code; } + + /// Returns the position of the point marked by ^ (or $name^) in the text. + /// Crashes if there isn't exactly one. + size_t point(llvm::StringRef Name = "") const; + /// Returns the position of all points marked by ^ (or $name^) in the text. + std::vector<size_t> points(llvm::StringRef Name = "") const; + + /// Returns the location of the range marked by [[ ]] (or $name[[ ]]). + /// Crashes if there isn't exactly one. + Range range(llvm::StringRef Name = "") const; + /// Returns the location of all ranges marked by [[ ]] (or $name[[ ]]). + std::vector<Range> ranges(llvm::StringRef Name = "") const; + +private: + std::string Code; + llvm::StringMap<llvm::SmallVector<size_t, 1>> Points; + llvm::StringMap<llvm::SmallVector<Range, 1>> Ranges; +}; + +} // namespace llvm + +#endif Index: clang/unittests/Sema/CodeCompleteTest.cpp =================================================================== --- clang/unittests/Sema/CodeCompleteTest.cpp +++ clang/unittests/Sema/CodeCompleteTest.cpp @@ -13,6 +13,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Tooling/Tooling.h" +#include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include <cstddef> @@ -109,41 +110,18 @@ return ResultCtx; } -struct ParsedAnnotations { - std::vector<size_t> Points; - std::string Code; -}; - -ParsedAnnotations parseAnnotations(StringRef AnnotatedCode) { - ParsedAnnotations R; - while (!AnnotatedCode.empty()) { - size_t NextPoint = AnnotatedCode.find('^'); - if (NextPoint == StringRef::npos) { - R.Code += AnnotatedCode; - AnnotatedCode = ""; - break; - } - R.Code += AnnotatedCode.substr(0, NextPoint); - R.Points.push_back(R.Code.size()); - - AnnotatedCode = AnnotatedCode.substr(NextPoint + 1); - } - return R; -} - CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) { - ParsedAnnotations P = parseAnnotations(AnnotatedCode); - assert(P.Points.size() == 1 && "expected exactly one annotation point"); - return runCompletion(P.Code, P.Points.front()); + llvm::Annotations A(AnnotatedCode); + return runCompletion(A.code(), A.point()); } std::vector<std::string> collectPreferredTypes(StringRef AnnotatedCode, std::string *PtrDiffType = nullptr) { - ParsedAnnotations P = parseAnnotations(AnnotatedCode); + llvm::Annotations A(AnnotatedCode); std::vector<std::string> Types; - for (size_t Point : P.Points) { - auto Results = runCompletion(P.Code, Point); + for (size_t Point : A.points()) { + auto Results = runCompletion(A.code(), Point); if (PtrDiffType) { assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType); *PtrDiffType = Results.PtrDiffType; Index: clang/unittests/Sema/CMakeLists.txt =================================================================== --- clang/unittests/Sema/CMakeLists.txt +++ clang/unittests/Sema/CMakeLists.txt @@ -16,4 +16,5 @@ clangSema clangSerialization clangTooling + LLVMTestingSupport ) Index: clang-tools-extra/unittests/clangd/Annotations.h =================================================================== --- clang-tools-extra/unittests/clangd/Annotations.h +++ clang-tools-extra/unittests/clangd/Annotations.h @@ -5,64 +5,32 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// -// Annotations lets you mark points and ranges inside source code, for tests: -// -// Annotations Example(R"cpp( -// int complete() { x.pri^ } // ^ indicates a point -// void err() { [["hello" == 42]]; } // [[this is a range]] -// $definition^class Foo{}; // points can be named: "definition" -// $fail[[static_assert(false, "")]] // ranges can be named too: "fail" -// )cpp"); -// -// StringRef Code = Example.code(); // annotations stripped. -// std::vector<Position> PP = Example.points(); // all unnamed points -// Position P = Example.point(); // there must be exactly one -// Range R = Example.range("fail"); // find named ranges -// -// Points/ranges are coordinates into `code()` which is stripped of annotations. -// -// Ranges may be nested (and points can be inside ranges), but there's no way -// to define general overlapping ranges. -// +// A clangd-specific version of llvm/Testing/Support/Annotations.h, replaces +// offsets and offset-based ranges with types from the LSP protocol. //===---------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H #include "Protocol.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" +#include "llvm/Testing/Support/Annotations.h" namespace clang { namespace clangd { -class Annotations { -public: - // Parses the annotations from Text. Crashes if it's malformed. - Annotations(llvm::StringRef Text); +/// Same as llvm::Annotations, but adjusts functions to LSP-specific types for +/// positions and ranges. +class Annotations : public llvm::Annotations { + using Base = llvm::Annotations; - // The input text with all annotations stripped. - // All points and ranges are relative to this stripped text. - llvm::StringRef code() const { return Code; } +public: + using llvm::Annotations::Annotations; - // Returns the position of the point marked by ^ (or $name^) in the text. - // Crashes if there isn't exactly one. Position point(llvm::StringRef Name = "") const; - // Returns the position of all points marked by ^ (or $name^) in the text. std::vector<Position> points(llvm::StringRef Name = "") const; - // Returns the location of the range marked by [[ ]] (or $name[[ ]]). - // Crashes if there isn't exactly one. - Range range(llvm::StringRef Name = "") const; - // Returns the location of all ranges marked by [[ ]] (or $name[[ ]]). - std::vector<Range> ranges(llvm::StringRef Name = "") const; - -private: - std::string Code; - llvm::StringMap<llvm::SmallVector<Position, 1>> Points; - llvm::StringMap<llvm::SmallVector<Range, 1>> Ranges; + clangd::Range range(llvm::StringRef Name = "") const; + std::vector<clangd::Range> ranges(llvm::StringRef Name = "") const; }; } // namespace clangd Index: clang-tools-extra/unittests/clangd/Annotations.cpp =================================================================== --- clang-tools-extra/unittests/clangd/Annotations.cpp +++ clang-tools-extra/unittests/clangd/Annotations.cpp @@ -12,74 +12,41 @@ namespace clang { namespace clangd { -// Crash if the assertion fails, printing the message and testcase. -// More elegant error handling isn't needed for unit tests. -static void require(bool Assertion, const char *Msg, llvm::StringRef Code) { - if (!Assertion) { - llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n"; - llvm_unreachable("Annotated testcase assertion failed!"); - } +Position Annotations::point(llvm::StringRef Name) const { + return offsetToPosition(code(), Base::point(Name)); } -Annotations::Annotations(llvm::StringRef Text) { - auto Here = [this] { return offsetToPosition(Code, Code.size()); }; - auto Require = [Text](bool Assertion, const char *Msg) { - require(Assertion, Msg, Text); - }; - llvm::Optional<llvm::StringRef> Name; - llvm::SmallVector<std::pair<llvm::StringRef, Position>, 8> OpenRanges; +std::vector<Position> Annotations::points(llvm::StringRef Name) const { + auto Offsets = Base::points(Name); - Code.reserve(Text.size()); - while (!Text.empty()) { - if (Text.consume_front("^")) { - Points[Name.getValueOr("")].push_back(Here()); - Name = None; - continue; - } - if (Text.consume_front("[[")) { - OpenRanges.emplace_back(Name.getValueOr(""), Here()); - Name = None; - continue; - } - Require(!Name, "$name should be followed by ^ or [["); - if (Text.consume_front("]]")) { - Require(!OpenRanges.empty(), "unmatched ]]"); - Ranges[OpenRanges.back().first].push_back( - {OpenRanges.back().second, Here()}); - OpenRanges.pop_back(); - continue; - } - if (Text.consume_front("$")) { - Name = Text.take_while(llvm::isAlnum); - Text = Text.drop_front(Name->size()); - continue; - } - Code.push_back(Text.front()); - Text = Text.drop_front(); - } - Require(!Name, "unterminated $name"); - Require(OpenRanges.empty(), "unmatched [["); -} + std::vector<Position> Ps; + Ps.reserve(Offsets.size()); + for (size_t O : Offsets) + Ps.push_back(offsetToPosition(code(), O)); -Position Annotations::point(llvm::StringRef Name) const { - auto I = Points.find(Name); - require(I != Points.end() && I->getValue().size() == 1, - "expected exactly one point", Code); - return I->getValue()[0]; + return Ps; } -std::vector<Position> Annotations::points(llvm::StringRef Name) const { - auto P = Points.lookup(Name); - return {P.begin(), P.end()}; + +static clangd::Range toLSPRange(llvm::StringRef Code, Annotations::Range R) { + clangd::Range LSPRange; + LSPRange.start = offsetToPosition(Code, R.Begin); + LSPRange.end = offsetToPosition(Code, R.End); + return LSPRange; } -Range Annotations::range(llvm::StringRef Name) const { - auto I = Ranges.find(Name); - require(I != Ranges.end() && I->getValue().size() == 1, - "expected exactly one range", Code); - return I->getValue()[0]; + +clangd::Range Annotations::range(llvm::StringRef Name) const { + return toLSPRange(code(), Base::range(Name)); } -std::vector<Range> Annotations::ranges(llvm::StringRef Name) const { - auto R = Ranges.lookup(Name); - return {R.begin(), R.end()}; + +std::vector<clangd::Range> Annotations::ranges(llvm::StringRef Name) const { + auto OffsetRanges = Base::ranges(Name); + + std::vector<clangd::Range> Rs; + Rs.reserve(OffsetRanges.size()); + for (Annotations::Range R : OffsetRanges) + Rs.push_back(toLSPRange(code(), R)); + + return Rs; } } // namespace clangd
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits