The goal of this patch is to make it easier to reason about the state of ObjCLanguage::MethodName. I do that in several ways: - Instead of using the constructor directly, you go through a factory method. It returns a std::optional<MethodName> so either you got an ObjCLanguage::MethodName or you didn't. No more checking if it's valid to know if you can use it or not. - ObjCLanguage::MethodName is now immutable. You cannot change its internals once it is created. - ObjCLanguage::MethodName::GetFullNameWithoutCategory previously had a parameter that let you get back an empty string if the method had no category. Every caller of this method was enabling this behavior so I dropped the parameter and made it the default behavior. - No longer store all the various components of the method name as ConstStrings. The relevant `Get` methods now return llvm::StringRefs backed by the MethodName's internal storage. The lifetime of these StringRefs are tied to the MethodName itself, so if you need to persist these you need to create copies. Differential Revision: https://reviews.llvm.org/D149914
115 lines
4.5 KiB
C++
115 lines
4.5 KiB
C++
//===-- ObjCLanguageTest.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 "Plugins/Language/ObjC/ObjCLanguage.h"
|
|
#include "lldb/lldb-enumerations.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <optional>
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
using namespace lldb_private;
|
|
|
|
TEST(ObjCLanguage, MethodNameParsing) {
|
|
struct TestCase {
|
|
llvm::StringRef input;
|
|
llvm::StringRef full_name_sans_category;
|
|
llvm::StringRef class_name;
|
|
llvm::StringRef class_name_with_category;
|
|
llvm::StringRef category;
|
|
llvm::StringRef selector;
|
|
};
|
|
|
|
TestCase strict_cases[] = {
|
|
{"-[MyClass mySelector:]", "", "MyClass", "MyClass", "", "mySelector:"},
|
|
{"+[MyClass mySelector:]", "", "MyClass", "MyClass", "", "mySelector:"},
|
|
{"-[MyClass(my_category) mySelector:]", "-[MyClass mySelector:]",
|
|
"MyClass", "MyClass(my_category)", "my_category", "mySelector:"},
|
|
{"+[MyClass(my_category) mySelector:]", "+[MyClass mySelector:]",
|
|
"MyClass", "MyClass(my_category)", "my_category", "mySelector:"},
|
|
};
|
|
|
|
TestCase lax_cases[] = {
|
|
{"[MyClass mySelector:]", "", "MyClass", "MyClass", "", "mySelector:"},
|
|
{"[MyClass(my_category) mySelector:]", "[MyClass mySelector:]", "MyClass",
|
|
"MyClass(my_category)", "my_category", "mySelector:"},
|
|
};
|
|
|
|
// First, be strict
|
|
for (const auto &test : strict_cases) {
|
|
std::optional<const ObjCLanguage::MethodName> method =
|
|
ObjCLanguage::MethodName::Create(test.input, /*strict = */ true);
|
|
EXPECT_TRUE(method.has_value());
|
|
EXPECT_EQ(test.full_name_sans_category,
|
|
method->GetFullNameWithoutCategory());
|
|
EXPECT_EQ(test.class_name, method->GetClassName());
|
|
EXPECT_EQ(test.class_name_with_category,
|
|
method->GetClassNameWithCategory());
|
|
EXPECT_EQ(test.category, method->GetCategory());
|
|
EXPECT_EQ(test.selector, method->GetSelector());
|
|
}
|
|
|
|
// We should make sure strict parsing does not accept lax cases
|
|
for (const auto &test : lax_cases) {
|
|
std::optional<const ObjCLanguage::MethodName> method =
|
|
ObjCLanguage::MethodName::Create(test.input, /*strict = */ true);
|
|
EXPECT_FALSE(method.has_value());
|
|
}
|
|
|
|
// All strict cases should work when not lax
|
|
for (const auto &test : strict_cases) {
|
|
std::optional<const ObjCLanguage::MethodName> method =
|
|
ObjCLanguage::MethodName::Create(test.input, /*strict = */ false);
|
|
EXPECT_TRUE(method.has_value());
|
|
EXPECT_EQ(test.full_name_sans_category,
|
|
method->GetFullNameWithoutCategory());
|
|
EXPECT_EQ(test.class_name, method->GetClassName());
|
|
EXPECT_EQ(test.class_name_with_category,
|
|
method->GetClassNameWithCategory());
|
|
EXPECT_EQ(test.category, method->GetCategory());
|
|
EXPECT_EQ(test.selector, method->GetSelector());
|
|
}
|
|
|
|
// Make sure non-strict parsing works
|
|
for (const auto &test : lax_cases) {
|
|
std::optional<const ObjCLanguage::MethodName> method =
|
|
ObjCLanguage::MethodName::Create(test.input, /*strict = */ false);
|
|
EXPECT_TRUE(method.has_value());
|
|
EXPECT_EQ(test.full_name_sans_category,
|
|
method->GetFullNameWithoutCategory());
|
|
EXPECT_EQ(test.class_name, method->GetClassName());
|
|
EXPECT_EQ(test.class_name_with_category,
|
|
method->GetClassNameWithCategory());
|
|
EXPECT_EQ(test.category, method->GetCategory());
|
|
EXPECT_EQ(test.selector, method->GetSelector());
|
|
}
|
|
}
|
|
|
|
TEST(ObjCLanguage, InvalidMethodNameParsing) {
|
|
// Tests that we correctly reject malformed function names
|
|
|
|
llvm::StringRef test_cases[] = {"+[Uh oh!",
|
|
"-[Definitely not...",
|
|
"[Nice try ] :)",
|
|
"+MaybeIfYouSquintYourEyes]",
|
|
"?[Tricky]",
|
|
"+[]",
|
|
"-[]",
|
|
"[]"};
|
|
|
|
for (const auto &name : test_cases) {
|
|
std::optional<const ObjCLanguage::MethodName> strict_method =
|
|
ObjCLanguage::MethodName::Create(name, /*strict = */ false);
|
|
EXPECT_FALSE(strict_method.has_value());
|
|
|
|
std::optional<const ObjCLanguage::MethodName> lax_method =
|
|
ObjCLanguage::MethodName::Create(name, /*strict = */ false);
|
|
EXPECT_FALSE(lax_method.has_value());
|
|
}
|
|
}
|