diff --git a/lldb/include/lldb/Utility/SourceLocationSpec.h b/lldb/include/lldb/Utility/SourceLocationSpec.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Utility/SourceLocationSpec.h @@ -0,0 +1,192 @@ +//===-- SourceLocationSpec.h ------------------------------------*- 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 LLDB_UTILITY_SOURCELOCATIONSPEC_H +#define LLDB_UTILITY_SOURCELOCATIONSPEC_H + +#include + +#include "lldb/Utility/FileSpec.h" + +#include "llvm/ADT/Optional.h" + +#include +#include + +namespace lldb_private { + +/// \class SourceLocationSpec SourceLocationSpec.h +/// "lldb/Utility/SourceLocationSpec.h" A source location utility class. +/// +/// A source location specifier class that holds a FileSpec object with line and +/// column information. The column line is optional. +class SourceLocationSpec { +public: + SourceLocationSpec() = delete; + + /// Constructor. + /// + /// Takes a \a file_spec with a \a line number and a \a column number. If + /// \a column is null or not provided, it is set to llvm::None. + /// + /// \param[in] file_spec + /// The full or partial path to a file. + /// + /// \param[in] line + /// The line number in the source file. + /// + /// \param[in] column + /// The column number in the line of the source file. + /// + /// + explicit SourceLocationSpec(FileSpec file_spec, uint32_t line); + explicit SourceLocationSpec(FileSpec file_spec, uint32_t line, + uint16_t column); + + /// Equal to operator + /// + /// Tests if this object is equal to \a rhs. + /// + /// \param[in] rhs + /// A const SourceLocationSpec object reference to compare this object + /// to. + /// + /// \return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + bool operator==(const SourceLocationSpec &rhs) const; + + /// Not equal to operator + /// + /// Tests if this object is not equal to \a rhs. + /// + /// \param[in] rhs + /// A const SourceLocationSpec object reference to compare this object + /// to. + /// + /// \return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + bool operator!=(const SourceLocationSpec &rhs) const; + + /// Less than to operator + /// + /// Tests if this object is less than \a rhs. + /// + /// \param[in] rhs + /// A const SourceLocationSpec object reference to compare this object + /// to. + /// + /// \return + /// \b true if this object is less than \a rhs, \b false + /// otherwise. + bool operator<(const SourceLocationSpec &rhs) const; + + /// Convert to pointer operator. + /// + /// This allows code to check a SourceLocationSpec object to see if it + /// contains anything valid using code such as: + /// + /// \code + /// SourceLocationSpec location_spec(...); + /// if (location_spec) + /// { ... + /// \endcode + /// + /// \return + /// A pointer to this object if both the file_spec and the line are valid, + /// nullptr otherwise. + explicit operator bool() const; + + /// Logical NOT operator. + /// + /// This allows code to check a SourceLocationSpec object to see if it is + /// invalid using code such as: + /// + /// \code + /// SourceLocationSpec location_spec(...); + /// if (!location_spec) + /// { ... + /// \endcode + /// + /// \return + /// Returns \b true if the object has an invalid file_spec or line number, + /// \b false otherwise. + bool operator!() const; + + /// Clears the object state. + /// + /// Clear this object by releasing the file_spec object + /// and making both line and column value the empty string. + void Clear(); + + /// Compare two SourceLocationSpec objects. + /// + /// If \a full is true, then the file_spec, the line and column must match. + /// If \a full is false, then only the file_spec and line number for \a lhs + /// and \a rhs are compared. This allows a SourceLocationSpec object that have + /// no column information to match a SourceLocationSpec objects that have + /// column information with matching file_spec and line component. + /// + /// \param[in] lhs + /// A const reference to the Left Hand Side object to compare. + /// + /// \param[in] rhs + /// A const reference to the Right Hand Side object to compare. + /// + /// \param[in] full + /// If true, then the file_spec, the line and column must match for a + /// compare to return zero (equal to). If false, then only the file_spec + /// and line number for \a lhs and \a rhs are compared, else a full + /// comparison is done. + /// + /// \return -1 if \a lhs is less than \a rhs, 0 if \a lhs is equal to \a rhs, + /// 1 if \a lhs is greater than \a rhs + static int Compare(const SourceLocationSpec &lhs, + const SourceLocationSpec &rhs, bool full); + + static bool Equal(const SourceLocationSpec &a, const SourceLocationSpec &b, + bool full); + + /// Match SourceLocationSpec \a pattern against SourceLocationSpec \a + /// location. If \a pattern has a column number, then the \a location must + /// have the same column number. Otherwise, it should only matches just the + /// file_spec and line number. An empty \a pattern matches everything. + static bool Match(const SourceLocationSpec &pattern, + const SourceLocationSpec &location); + + /// Dump this object to a Stream. + /// + /// Dump the object to the supplied stream \a s, starting with the file name, + /// then the line number and if available the column number. + /// + /// \param[in] s + /// The stream to which to dump the object description. + void Dump(llvm::raw_ostream &s) const; + + const char *GetCString() const; + + FileSpec GetFileSpec() const { return m_file_spec; } + + uint32_t GetLine() const { return m_line; } + + bool HasColumn() const { return m_column.hasValue() && *m_column != 0; } + + llvm::Optional GetColumn() const { return m_column; } + +protected: + FileSpec m_file_spec; + uint32_t m_line; + llvm::Optional m_column; +}; + +/// Dump a SourceLocationSpec object to a stream +Stream &operator<<(Stream &s, const SourceLocationSpec &loc); +} // namespace lldb_private + +#endif // LLDB_UTILITY_SOURCELOCATIONSPEC_H diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -54,6 +54,7 @@ Scalar.cpp SelectHelper.cpp State.cpp + SourceLocationSpec.cpp Status.cpp Stream.cpp StreamCallback.cpp diff --git a/lldb/source/Utility/SourceLocationSpec.cpp b/lldb/source/Utility/SourceLocationSpec.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Utility/SourceLocationSpec.cpp @@ -0,0 +1,97 @@ +//===-- SourceLocationSpec.cpp --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/SourceLocationSpec.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +SourceLocationSpec::SourceLocationSpec(FileSpec file_spec, uint32_t line) + : m_file_spec(file_spec), m_line(line), m_column(llvm::None) {} + +SourceLocationSpec::SourceLocationSpec(FileSpec file_spec, uint32_t line, + uint16_t column) + : SourceLocationSpec(file_spec, line) { + if (column) + m_column = column; +} + +SourceLocationSpec::operator bool() const { return m_file_spec && m_line; } + +bool SourceLocationSpec::operator!() const { return !operator bool(); } + +bool SourceLocationSpec::operator==(const SourceLocationSpec &rhs) const { + return m_file_spec == rhs.GetFileSpec() && m_line == rhs.GetLine() && + m_column == rhs.GetColumn(); +} + +bool SourceLocationSpec::operator!=(const SourceLocationSpec &rhs) const { + return !(*this == rhs); +} + +bool SourceLocationSpec::operator<(const SourceLocationSpec &rhs) const { + return SourceLocationSpec::Compare(*this, rhs, true) < 0; +} + +Stream &lldb_private::operator<<(Stream &s, const SourceLocationSpec &loc) { + loc.Dump(s.AsRawOstream()); + return s; +} + +void SourceLocationSpec::Clear() { + m_file_spec.Clear(); + m_line = 0; + m_column = llvm::None; +} + +int SourceLocationSpec::Compare(const SourceLocationSpec &a, + const SourceLocationSpec &b, bool full) { + + if (int result = FileSpec::Compare(a.GetFileSpec(), b.GetFileSpec(), full)) + return result; + + // If both file_spec matches, compare line numbers. + if (a.GetLine() != b.GetLine()) { + return (a.GetLine() < b.GetLine()) ? -1 : 1; + } + + // If both file_spec and line number match, and \a full comparaison is + // enabled, compare column number. + if (full && a.GetColumn() != b.GetColumn()) + return (a.GetColumn() < b.GetColumn()) ? -1 : 1; + + return 0; +} + +bool SourceLocationSpec::Equal(const SourceLocationSpec &a, + const SourceLocationSpec &b, bool full) { + return full ? a == b : Match(a, b); +} + +bool SourceLocationSpec::Match(const SourceLocationSpec &pattern, + const SourceLocationSpec &location) { + return pattern.GetFileSpec() == location.GetFileSpec() && + pattern.GetLine() == location.GetLine(); +} + +void SourceLocationSpec::Dump(llvm::raw_ostream &s) const { + GetFileSpec().Dump(s); + s << ':' << GetLine(); + + if (HasColumn()) + s << ':' << GetColumn(); +} + +const char *SourceLocationSpec::GetCString() const { + std::string data; + llvm::raw_string_ostream ss(data); + Dump(ss); + + return ConstString{ss.str()}.AsCString(); +} diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt --- a/lldb/unittests/Utility/CMakeLists.txt +++ b/lldb/unittests/Utility/CMakeLists.txt @@ -25,6 +25,7 @@ ReproducerTest.cpp ScalarTest.cpp SharedClusterTest.cpp + SourceLocationSpecTest.cpp StateTest.cpp StatusTest.cpp StreamTeeTest.cpp @@ -38,9 +39,9 @@ TildeExpressionResolverTest.cpp TimeoutTest.cpp TimerTest.cpp + UUIDTest.cpp UriParserTest.cpp UserIDResolverTest.cpp - UUIDTest.cpp VASprintfTest.cpp VMRangeTest.cpp XcodeSDKTest.cpp diff --git a/lldb/unittests/Utility/SourceLocationSpecTest.cpp b/lldb/unittests/Utility/SourceLocationSpecTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Utility/SourceLocationSpecTest.cpp @@ -0,0 +1,210 @@ +//===-- SourceLocationSpecTest.cpp +//--------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Utility/SourceLocationSpec.h" + +using namespace lldb_private; + +TEST(SourceLocationSpecTest, FileLineColumnComponents) { + FileSpec fs("/foo/bar", FileSpec::Style::posix); + const uint32_t line = 19; + const uint16_t column = 4; + SourceLocationSpec without_column(fs, line); + EXPECT_EQ(fs, without_column.GetFileSpec()); + EXPECT_EQ(line, without_column.GetLine()); + EXPECT_FALSE(without_column.HasColumn()); + EXPECT_EQ(llvm::None, without_column.GetColumn()); + EXPECT_STREQ("/foo/bar:19", without_column.GetCString()); + + SourceLocationSpec with_column(fs, 19, 4); + EXPECT_TRUE(with_column.HasColumn()); + EXPECT_EQ(column, with_column.GetColumn()); + EXPECT_STREQ("/foo/bar:19:4", with_column.GetCString()); +} + +TEST(SourceLocationSpecTest, Equal) { + auto Eq = [](SourceLocationSpec a, SourceLocationSpec b, bool full) { + return SourceLocationSpec::Equal(a, b, full); + }; + + FileSpec fs("/foo/bar", FileSpec::Style::posix); + FileSpec other_fs("/foo/baz", FileSpec::Style::posix); + + EXPECT_TRUE(Eq(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4), true)); + EXPECT_TRUE(Eq(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4), false)); + + EXPECT_FALSE( + Eq(SourceLocationSpec(fs, 4), SourceLocationSpec(other_fs, 4), true)); + EXPECT_FALSE( + Eq(SourceLocationSpec(fs, 4), SourceLocationSpec(other_fs, 4), false)); + + EXPECT_TRUE( + Eq(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4, 19), true)); + EXPECT_TRUE( + Eq(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4, 19), false)); + + EXPECT_FALSE(Eq(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(other_fs, 4, 19), true)); + EXPECT_FALSE(Eq(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(other_fs, 4, 19), false)); + + EXPECT_FALSE( + Eq(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4, 19), true)); + EXPECT_TRUE( + Eq(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4, 19), false)); + + EXPECT_FALSE(Eq(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4), true)); + EXPECT_FALSE(Eq(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4), false)); + + EXPECT_FALSE( + Eq(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4, 19), true)); + EXPECT_FALSE( + Eq(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4, 19), false)); + + EXPECT_FALSE( + Eq(SourceLocationSpec(fs, 4, 96), SourceLocationSpec(fs, 4, 19), true)); + EXPECT_TRUE( + Eq(SourceLocationSpec(fs, 4, 96), SourceLocationSpec(fs, 4, 19), false)); +} + +TEST(SourceLocationSpecTest, Match) { + auto Match = [](SourceLocationSpec a, SourceLocationSpec b) { + return SourceLocationSpec::Match(a, b); + }; + + FileSpec fs("/foo/bar", FileSpec::Style::posix); + FileSpec other_fs("/foo/baz", FileSpec::Style::posix); + + EXPECT_TRUE(Match(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4))); + + EXPECT_FALSE( + Match(SourceLocationSpec(fs, 4), SourceLocationSpec(other_fs, 4))); + EXPECT_FALSE(Match(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 19))); + EXPECT_FALSE( + Match(SourceLocationSpec(fs, 4), SourceLocationSpec(other_fs, 19))); + + EXPECT_TRUE( + Match(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4, 19))); + EXPECT_TRUE( + Match(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4, 96))); + + EXPECT_FALSE(Match(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(other_fs, 4, 96))); + EXPECT_FALSE( + Match(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 19, 96))); + EXPECT_FALSE(Match(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(other_fs, 19, 96))); + + EXPECT_TRUE(Match(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4, 19))); + EXPECT_TRUE(Match(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4))); + + EXPECT_FALSE( + Match(SourceLocationSpec(other_fs, 4), SourceLocationSpec(fs, 4, 19))); + EXPECT_FALSE( + Match(SourceLocationSpec(other_fs, 4, 19), SourceLocationSpec(fs, 4))); +} + +TEST(SourceLocationSpecTest, Compare) { + auto Cmp = [](SourceLocationSpec a, SourceLocationSpec b, bool full) { + return SourceLocationSpec::Compare(a, b, full); + }; + + FileSpec fs("/foo/bar", FileSpec::Style::posix); + FileSpec other_fs("/foo/baz", FileSpec::Style::posix); + + // Asymetric comparaison + EXPECT_EQ( + -1, Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4, 19), true)); + EXPECT_EQ( + 0, Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4, 19), false)); + EXPECT_EQ( + 1, Cmp(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4), true)); + + // mutable FS, const Line + EXPECT_EQ(-1, Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(other_fs, 4), + true)); + EXPECT_EQ(-1, Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(other_fs, 4), + false)); + EXPECT_EQ( + 1, Cmp(SourceLocationSpec(other_fs, 4), SourceLocationSpec(fs, 4), true)); + EXPECT_EQ(1, Cmp(SourceLocationSpec(other_fs, 4), SourceLocationSpec(fs, 4), + false)); + + // const FS, mutable Line + EXPECT_EQ(-1, + Cmp(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4), true)); + EXPECT_EQ(-1, + Cmp(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4), false)); + EXPECT_EQ(0, Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4), true)); + EXPECT_EQ(0, + Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 4), false)); + EXPECT_EQ(1, Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 0), true)); + EXPECT_EQ(1, + Cmp(SourceLocationSpec(fs, 4), SourceLocationSpec(fs, 0), false)); + + // const FS, mutable Line, const Column + EXPECT_EQ( + -1, Cmp(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4, 19), true)); + EXPECT_EQ( + -1, Cmp(SourceLocationSpec(fs, 0), SourceLocationSpec(fs, 4, 19), false)); + EXPECT_EQ( + 1, Cmp(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 0), true)); + EXPECT_EQ( + 1, Cmp(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 0), false)); + + // mutable FS, const Line, const Column + EXPECT_EQ(-1, Cmp(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(other_fs, 4, 19), true)); + EXPECT_EQ(-1, Cmp(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(other_fs, 4, 19), false)); + EXPECT_EQ(0, Cmp(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4, 19), + true)); + EXPECT_EQ(0, Cmp(SourceLocationSpec(fs, 4, 19), SourceLocationSpec(fs, 4, 19), + false)); + EXPECT_EQ(1, Cmp(SourceLocationSpec(other_fs, 4, 19), + SourceLocationSpec(fs, 4, 19), true)); + EXPECT_EQ(1, Cmp(SourceLocationSpec(other_fs, 4, 19), + SourceLocationSpec(fs, 4, 19), false)); + + // const FS, const Line, mutable Column + EXPECT_EQ(-1, Cmp(SourceLocationSpec(fs, 4, 19), + SourceLocationSpec(fs, 4, 96), true)); + EXPECT_EQ(0, Cmp(SourceLocationSpec(fs, 4, 96), SourceLocationSpec(fs, 4, 19), + false)); + EXPECT_EQ(1, Cmp(SourceLocationSpec(fs, 4, 96), SourceLocationSpec(fs, 4, 19), + true)); +} + +TEST(SourceLocationSpecTest, OperatorBool) { + SourceLocationSpec invalid(FileSpec(), 0); + EXPECT_FALSE(invalid); + + SourceLocationSpec invalid_filespec(FileSpec(), 4); + EXPECT_FALSE(invalid_filespec); + + SourceLocationSpec invalid_line(FileSpec("/foo/bar"), 0); + EXPECT_FALSE(invalid_line); + + SourceLocationSpec valid_fs_line_no_column(FileSpec("/foo/bar"), 4); + EXPECT_TRUE(valid_fs_line_no_column); + + SourceLocationSpec invalid_fs_column(FileSpec(), 4, 0); + EXPECT_FALSE(invalid_fs_column); + + SourceLocationSpec invalid_line_column(FileSpec("/foo/bar"), 0, 19); + EXPECT_FALSE(invalid_line_column); + + SourceLocationSpec valid_fs_line_zero_column(FileSpec("/foo/bar"), 4, 0); + EXPECT_TRUE(valid_fs_line_zero_column); + + SourceLocationSpec valid_fs_line_column(FileSpec("/foo/bar"), 4, 19); + EXPECT_TRUE(valid_fs_line_column); +}