diff --git a/lldb/include/lldb/Core/SourceLocationSpec.h b/lldb/include/lldb/Core/SourceLocationSpec.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Core/SourceLocationSpec.h @@ -0,0 +1,188 @@ +//===-- 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 "lldb/Core/Declaration.h" +#include "lldb/lldb-defines.h" +#include "llvm/ADT/Optional.h" + +#include + +namespace lldb_private { + +/// \class SourceLocationSpec SourceLocationSpec.h +/// "lldb/Core/SourceLocationSpec.h" A source location specifier class. +/// +/// A source location specifier class that holds a Declaration object containing +/// a FileSpec with line and column information. The column line is optional. +/// It also holds search flags that can be fetched by resolvers to look inlined +/// declarations and/or exact matches. +class SourceLocationSpec { +public: + /// 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. + /// + /// \param[in] check_inlines + /// Whether to look for a match in inlined declaration. + /// + /// \param[in] exact_match + /// Whether to look for an exact match. + /// + explicit SourceLocationSpec(FileSpec file_spec, uint32_t line, + llvm::Optional column = llvm::None, + bool check_inlines = false, + bool exact_match = false); + + SourceLocationSpec() = delete; + + /// Convert to boolean 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; + + /// 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; + + /// 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); + + static bool Equal(const SourceLocationSpec &lhs, + const SourceLocationSpec &rhs, bool full); + + /// 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(Stream &s) const; + + std::string GetString() const; + + FileSpec GetFileSpec() const { return m_declaration.GetFile(); } + + llvm::Optional GetLine() const; + + llvm::Optional GetColumn() const; + + bool GetCheckInlines() const { return m_check_inlines; } + + bool GetExactMatch() const { return m_exact_match; } + +protected: + Declaration m_declaration; + /// Tells if the resolver should look in inlined declaration. + bool m_check_inlines; + /// Tells if the resolver should look for an exact match. + bool m_exact_match; +}; + +/// 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/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -48,6 +48,7 @@ RichManglingContext.cpp SearchFilter.cpp Section.cpp + SourceLocationSpec.cpp SourceManager.cpp StreamAsynchronousIO.cpp StreamFile.cpp diff --git a/lldb/source/Core/SourceLocationSpec.cpp b/lldb/source/Core/SourceLocationSpec.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Core/SourceLocationSpec.cpp @@ -0,0 +1,81 @@ +//===-- 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/Core/SourceLocationSpec.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +SourceLocationSpec::SourceLocationSpec(FileSpec file_spec, uint32_t line, + llvm::Optional column, + bool check_inlines, bool exact_match) + : m_declaration(file_spec, line, + column.getValueOr(LLDB_INVALID_COLUMN_NUMBER)), + m_check_inlines(check_inlines), m_exact_match(exact_match) {} + +SourceLocationSpec::operator bool() const { return m_declaration.IsValid(); } + +bool SourceLocationSpec::operator!() const { return !operator bool(); } + +bool SourceLocationSpec::operator==(const SourceLocationSpec &rhs) const { + return m_declaration == rhs.m_declaration && + m_check_inlines == rhs.GetCheckInlines() && + m_exact_match == rhs.GetExactMatch(); +} + +bool SourceLocationSpec::operator!=(const SourceLocationSpec &rhs) const { + return !(*this == rhs); +} + +bool SourceLocationSpec::operator<(const SourceLocationSpec &rhs) const { + return SourceLocationSpec::Compare(*this, rhs) < 0; +} + +Stream &lldb_private::operator<<(Stream &s, const SourceLocationSpec &loc) { + loc.Dump(s); + return s; +} + +int SourceLocationSpec::Compare(const SourceLocationSpec &lhs, + const SourceLocationSpec &rhs) { + return Declaration::Compare(lhs.m_declaration, rhs.m_declaration); +} + +bool SourceLocationSpec::Equal(const SourceLocationSpec &lhs, + const SourceLocationSpec &rhs, bool full) { + return full ? lhs == rhs + : (lhs.GetFileSpec() == rhs.GetFileSpec() && + lhs.GetLine() == rhs.GetLine()); +} + +void SourceLocationSpec::Dump(Stream &s) const { + s << "check inlines = " << llvm::toStringRef(m_check_inlines); + s << ", exact match = " << llvm::toStringRef(m_exact_match); + m_declaration.Dump(&s, true); +} + +std::string SourceLocationSpec::GetString() const { + StreamString ss; + Dump(ss); + return ss.GetString().str(); +} + +llvm::Optional SourceLocationSpec::GetLine() const { + uint32_t line = m_declaration.GetLine(); + if (line == 0 || line == LLDB_INVALID_LINE_NUMBER) + return llvm::None; + return line; +} + +llvm::Optional SourceLocationSpec::GetColumn() const { + uint16_t column = m_declaration.GetColumn(); + if (column == LLDB_INVALID_COLUMN_NUMBER) + return llvm::None; + return column; +} diff --git a/lldb/unittests/Core/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt --- a/lldb/unittests/Core/CMakeLists.txt +++ b/lldb/unittests/Core/CMakeLists.txt @@ -5,6 +5,7 @@ MangledTest.cpp ModuleSpecTest.cpp RichManglingContextTest.cpp + SourceLocationSpecTest.cpp SourceManagerTest.cpp StreamCallbackTest.cpp UniqueCStringMapTest.cpp diff --git a/lldb/unittests/Core/SourceLocationSpecTest.cpp b/lldb/unittests/Core/SourceLocationSpecTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Core/SourceLocationSpecTest.cpp @@ -0,0 +1,183 @@ +//===-- 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/Core/SourceLocationSpec.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private; + +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); +} + +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, LLDB_INVALID_COLUMN_NUMBER, false, + true); + EXPECT_TRUE(without_column); + EXPECT_EQ(fs, without_column.GetFileSpec()); + EXPECT_EQ(line, without_column.GetLine().getValueOr(0)); + EXPECT_EQ(llvm::None, without_column.GetColumn()); + EXPECT_FALSE(without_column.GetCheckInlines()); + EXPECT_TRUE(without_column.GetExactMatch()); + EXPECT_STREQ("check inlines = false, exact match = true, decl = /foo/bar:19", + without_column.GetCString()); + + SourceLocationSpec with_column(fs, line, column, true, false); + EXPECT_TRUE(with_column); + EXPECT_EQ(column, *with_column.GetColumn()); + EXPECT_TRUE(with_column.GetCheckInlines()); + EXPECT_FALSE(with_column.GetExactMatch()); + EXPECT_STREQ( + "check inlines = true, exact match = false, decl = /foo/bar:19:4", + with_column.GetCString()); +} + +static SourceLocationSpec Create(bool check_inlines, bool exact_match, + FileSpec fs, uint32_t line, + uint16_t column = LLDB_INVALID_COLUMN_NUMBER) { + return SourceLocationSpec(fs, line, column, check_inlines, exact_match); +} + +TEST(SourceLocationSpecTest, Equal) { + auto Equal = [](SourceLocationSpec lhs, SourceLocationSpec rhs, bool full) { + return SourceLocationSpec::Equal(lhs, rhs, full); + }; + + const FileSpec fs("/foo/bar", FileSpec::Style::posix); + const FileSpec other_fs("/foo/baz", FileSpec::Style::posix); + + // mutating FileSpec + const Inlined, ExactMatch, Line + EXPECT_TRUE( + Equal(Create(false, false, fs, 4), Create(false, false, fs, 4), true)); + EXPECT_TRUE( + Equal(Create(true, true, fs, 4), Create(true, true, fs, 4), false)); + EXPECT_FALSE(Equal(Create(false, false, fs, 4), + Create(false, false, other_fs, 4), true)); + EXPECT_FALSE( + Equal(Create(true, true, fs, 4), Create(true, true, other_fs, 4), false)); + + // Mutating FileSpec + const Inlined, ExactMatch, Line, Column + EXPECT_TRUE(Equal(Create(false, false, fs, 4, 19), + Create(false, false, fs, 4, 19), true)); + EXPECT_TRUE(Equal(Create(true, true, fs, 4, 19), + Create(true, true, fs, 4, 19), false)); + EXPECT_FALSE(Equal(Create(false, false, fs, 4, 19), + Create(false, false, other_fs, 4, 19), true)); + EXPECT_FALSE(Equal(Create(true, true, fs, 4, 19), + Create(true, true, other_fs, 4, 19), false)); + + // Asymetric match + EXPECT_FALSE( + Equal(Create(true, true, fs, 4), Create(true, true, fs, 4, 19), true)); + EXPECT_TRUE(Equal(Create(false, false, fs, 4), + Create(false, false, fs, 4, 19), false)); + + // Mutating Inlined, ExactMatch + EXPECT_FALSE( + Equal(Create(true, false, fs, 4), Create(false, true, fs, 4), true)); + EXPECT_TRUE( + Equal(Create(false, true, fs, 4), Create(true, false, fs, 4), false)); + + // Mutating Column + EXPECT_FALSE(Equal(Create(true, true, fs, 4, 96), + Create(true, true, fs, 4, 19), true)); + EXPECT_TRUE(Equal(Create(false, false, fs, 4, 96), + Create(false, false, fs, 4, 19), false)); +} + +TEST(SourceLocationSpecTest, Compare) { + auto Cmp = [](SourceLocationSpec a, SourceLocationSpec b) { + return SourceLocationSpec::Compare(a, b); + }; + + FileSpec fs("/foo/bar", FileSpec::Style::posix); + FileSpec other_fs("/foo/baz", FileSpec::Style::posix); + + // Asymetric comparaison + EXPECT_EQ(-1, Cmp(Create(true, true, fs, 4), Create(true, true, fs, 4, 19))); + EXPECT_EQ(-1, + Cmp(Create(false, false, fs, 4), Create(false, false, fs, 4, 19))); + EXPECT_EQ(1, Cmp(Create(true, true, fs, 4, 19), Create(true, true, fs, 4))); + + // Mutating FS, const Line + EXPECT_EQ( + -1, Cmp(Create(false, false, fs, 4), Create(false, false, other_fs, 4))); + EXPECT_EQ(-1, + Cmp(Create(true, true, fs, 4), Create(true, true, other_fs, 4))); + EXPECT_EQ(1, + Cmp(Create(false, true, other_fs, 4), Create(false, true, fs, 4))); + EXPECT_EQ(1, + Cmp(Create(true, false, other_fs, 4), Create(true, false, fs, 4))); + + // Const FS, mutating Line + EXPECT_EQ(-1, Cmp(Create(false, false, fs, 1), Create(false, false, fs, 4))); + EXPECT_EQ(-1, Cmp(Create(true, true, fs, 1), Create(true, true, fs, 4))); + EXPECT_EQ(0, Cmp(Create(false, true, fs, 4), Create(false, true, fs, 4))); + EXPECT_EQ(0, Cmp(Create(true, false, fs, 4), Create(true, false, fs, 4))); + EXPECT_EQ(1, Cmp(Create(false, false, fs, 4), Create(false, false, fs, 1))); + EXPECT_EQ(1, Cmp(Create(true, true, fs, 4), Create(true, true, fs, 1))); + + // Const FS, mutating Line, const Column + EXPECT_EQ(-1, + Cmp(Create(false, true, fs, 1), Create(false, true, fs, 4, 19))); + EXPECT_EQ(-1, Cmp(Create(true, true, fs, 1), Create(true, true, fs, 4, 19))); + EXPECT_EQ(1, Cmp(Create(true, false, fs, 4, 19), Create(true, false, fs, 1))); + EXPECT_EQ(1, Cmp(Create(true, false, fs, 4, 19), Create(true, false, fs, 1))); + + // Mutating FS, const Line, const Column + EXPECT_EQ(-1, Cmp(Create(false, false, fs, 4, 19), + Create(false, false, other_fs, 4, 19))); + EXPECT_EQ(-1, Cmp(Create(true, true, fs, 4, 19), + Create(true, true, other_fs, 4, 19))); + EXPECT_EQ( + 0, Cmp(Create(false, false, fs, 4, 19), Create(false, false, fs, 4, 19))); + EXPECT_EQ(0, + Cmp(Create(true, true, fs, 4, 19), Create(true, true, fs, 4, 19))); + EXPECT_EQ(1, Cmp(Create(false, true, other_fs, 4, 19), + Create(false, true, fs, 4, 19))); + EXPECT_EQ(1, Cmp(Create(true, false, other_fs, 4, 19), + Create(true, false, fs, 4, 19))); + + // Const FS, const Line, mutating Column + EXPECT_EQ(-1, Cmp(Create(false, false, fs, 4, 19), + Create(false, false, fs, 4, 96))); + EXPECT_EQ(1, + Cmp(Create(true, true, fs, 4, 96), Create(true, true, fs, 4, 19))); + EXPECT_EQ( + 1, Cmp(Create(false, true, fs, 4, 96), Create(false, true, fs, 4, 19))); +}