-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[lldb] Support alternatives for scope format entries #137751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,16 +8,36 @@ | |
|
||
#include "lldb/Core/FormatEntity.h" | ||
#include "lldb/Utility/Status.h" | ||
|
||
#include "lldb/Utility/StreamString.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Support/Error.h" | ||
#include "llvm/Testing/Support/Error.h" | ||
#include "gtest/gtest.h" | ||
|
||
using namespace lldb_private; | ||
using namespace llvm; | ||
|
||
using Definition = FormatEntity::Entry::Definition; | ||
using Entry = FormatEntity::Entry; | ||
|
||
TEST(FormatEntityTest, DefinitionConstructionNameAndType) { | ||
namespace { | ||
class FormatEntityTest : public ::testing::Test { | ||
public: | ||
Expected<std::string> Format(StringRef format_str) { | ||
StreamString stream; | ||
FormatEntity::Entry format; | ||
Status status = FormatEntity::Parse(format_str, format); | ||
if (status.Fail()) | ||
return status.ToError(); | ||
|
||
FormatEntity::Format(format, stream, nullptr, nullptr, nullptr, nullptr, | ||
false, false); | ||
return stream.GetString().str(); | ||
} | ||
}; | ||
} // namespace | ||
|
||
TEST_F(FormatEntityTest, DefinitionConstructionNameAndType) { | ||
Definition d("foo", FormatEntity::Entry::Type::Invalid); | ||
|
||
EXPECT_STREQ(d.name, "foo"); | ||
|
@@ -29,7 +49,7 @@ TEST(FormatEntityTest, DefinitionConstructionNameAndType) { | |
EXPECT_FALSE(d.keep_separator); | ||
} | ||
|
||
TEST(FormatEntityTest, DefinitionConstructionNameAndString) { | ||
TEST_F(FormatEntityTest, DefinitionConstructionNameAndString) { | ||
Definition d("foo", "string"); | ||
|
||
EXPECT_STREQ(d.name, "foo"); | ||
|
@@ -41,7 +61,7 @@ TEST(FormatEntityTest, DefinitionConstructionNameAndString) { | |
EXPECT_FALSE(d.keep_separator); | ||
} | ||
|
||
TEST(FormatEntityTest, DefinitionConstructionNameTypeData) { | ||
TEST_F(FormatEntityTest, DefinitionConstructionNameTypeData) { | ||
Definition d("foo", FormatEntity::Entry::Type::Invalid, 33); | ||
|
||
EXPECT_STREQ(d.name, "foo"); | ||
|
@@ -53,7 +73,7 @@ TEST(FormatEntityTest, DefinitionConstructionNameTypeData) { | |
EXPECT_FALSE(d.keep_separator); | ||
} | ||
|
||
TEST(FormatEntityTest, DefinitionConstructionNameTypeChildren) { | ||
TEST_F(FormatEntityTest, DefinitionConstructionNameTypeChildren) { | ||
Definition d("foo", FormatEntity::Entry::Type::Invalid, 33); | ||
Definition parent("parent", FormatEntity::Entry::Type::Invalid, 1, &d); | ||
EXPECT_STREQ(parent.name, "parent"); | ||
|
@@ -153,10 +173,37 @@ constexpr llvm::StringRef lookupStrings[] = { | |
"${target.file.fullpath}", | ||
"${var.dummy-var-to-test-wildcard}"}; | ||
|
||
TEST(FormatEntity, LookupAllEntriesInTree) { | ||
TEST_F(FormatEntityTest, LookupAllEntriesInTree) { | ||
for (const llvm::StringRef testString : lookupStrings) { | ||
Entry e; | ||
EXPECT_TRUE(FormatEntity::Parse(testString, e).Success()) | ||
<< "Formatting " << testString << " did not succeed"; | ||
} | ||
} | ||
|
||
TEST_F(FormatEntityTest, Scope) { | ||
// Scope with one alternative. | ||
EXPECT_THAT_EXPECTED(Format("{${frame.pc}|foo}"), HasValue("foo")); | ||
|
||
// Scope with multiple alternatives. | ||
EXPECT_THAT_EXPECTED(Format("{${frame.pc}|${function.name}|foo}"), | ||
HasValue("foo")); | ||
|
||
// Escaped pipe inside a scope. | ||
EXPECT_THAT_EXPECTED(Format("{foo\\|bar}"), HasValue("foo|bar")); | ||
|
||
// Unescaped pipe outside a scope. | ||
EXPECT_THAT_EXPECTED(Format("foo|bar"), HasValue("foo|bar")); | ||
|
||
// Nested scopes. Note that scopes always resolve. | ||
EXPECT_THAT_EXPECTED(Format("{{${frame.pc}|foo}|{bar}}"), HasValue("foo")); | ||
EXPECT_THAT_EXPECTED(Format("{{${frame.pc}}|{bar}}"), HasValue("")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this resolve to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, because a scope always resolves successfully, no matter what's in it, this behaves as expected. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I figured this was a weird case which is why I included it :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh so within Does that mean the fallback can't ever be used between scopes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be difficult to make a scope fail if all its children failed? That would be more intuitive. But I suspect it might be a breaking change we're not willing to make? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would break literally every existing use of scopes because the only reason to use them was to make expansion failures non-fatal. I'm not saying we can't do that, but I think that's what it is. We could try to split hairs and say this only applies to scopes with more than one alternative, but I don't know if it's worth it. I suppose it might be used to write something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we maybe disallow alternatives to be used between scopes (or rather where a scope is on the left-hand side)? I guess technically it does "work", but one would have to know the intricacies of this language to understand why. Would there ever be a case where writing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm on the fence. It's probably not too hard to implement (although you need to be careful to only trigger this for an alternative scope, a regular nested scope is okay). On the other hand, if you know how a scope works, I think this behaves pretty much as expected, it's just a rather unhelpful thing to do. I don't think we have precedence for diagnosing something like this. I can see how "clean" or "ugly" it is to implement and that might sway me one way or another. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. If it's a pain to implement I won't insist. There is precedent for diagnosing invalid syntax (like mismatching curly braces). But maybe that's not the level at which this diagnostic would need to be implemented |
||
|
||
// Pipe between scopes. | ||
EXPECT_THAT_EXPECTED(Format("{foo}|{bar}"), HasValue("foo|bar")); | ||
EXPECT_THAT_EXPECTED(Format("{foo}||{bar}"), HasValue("foo||bar")); | ||
|
||
// Empty space between pipes. | ||
EXPECT_THAT_EXPECTED(Format("{{foo}||{bar}}"), HasValue("foo")); | ||
EXPECT_THAT_EXPECTED(Format("{${frame.pc}||{bar}}"), HasValue("")); | ||
} | ||
Michael137 marked this conversation as resolved.
Show resolved
Hide resolved
|
Uh oh!
There was an error while loading. Please reload this page.