Skip to content

[cxx-interop] Add snake_case to CXXMethodBridging #41905

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions include/swift/ClangImporter/CXXMethodBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ struct CXXMethodBridging {
if (nameIsBlacklist())
return Kind::unknown;

// this should be handled as snake case. See: rdar://89453010
// This should be handled as snake case. See: rdar://89453010
// case. In the future we could
// import these too, though.
auto nameKind = classifyNameKind();
if (nameKind != NameKind::title && nameKind != NameKind::camel &&
nameKind != NameKind::lower)
nameKind != NameKind::lower && nameKind != NameKind::snake)
return Kind::unknown;

if (getClangName().startswith_insensitive("set")) {
Expand Down Expand Up @@ -62,16 +62,18 @@ struct CXXMethodBridging {
}

NameKind classifyNameKind() {
bool allLower = llvm::all_of(getClangName(), islower);
bool hasUpper = llvm::any_of(
getClangName(), [](unsigned char ch) { return std::isupper(ch); });

if (getClangName().empty())
if (getClangName().empty()) {
return NameKind::unknown;
}

if (getClangName().contains('_'))
return allLower ? NameKind::snake : NameKind::unknown;

if (allLower)
if (getClangName().contains('_')) {
return hasUpper ? NameKind::unknown : NameKind::snake;
} else if (!hasUpper) {
return NameKind::lower;
}

return islower(getClangName().front()) ? NameKind::camel : NameKind::title;
}
Expand All @@ -83,7 +85,7 @@ struct CXXMethodBridging {
return method->getName();
}

// this should be handled as snake case. See: rdar://89453010
// This should be handled as snake case. See: rdar://89453010
std::string importNameAsCamelCaseName() {
std::string output;
auto kind = classify();
Expand All @@ -103,6 +105,24 @@ struct CXXMethodBridging {
// The first character is always lowercase.
output.front() = std::tolower(output.front());

if (classifyNameKind() == NameKind::snake) {
for (std::size_t i = 0; i < output.size(); i++) {
size_t next = i + 1;
if (output[i] == '_') {
// If the first or last element is an underscore, remove it.
if (i == 0 || next == output.size()) {
output.erase(i, 1);
} else if (next < output.size()) {
// If the current element is an underscore, capitalize the element
// next to it, and remove the extra element.
output[i] = std::toupper(output[next]);
output.erase(next, 1);
}
}
}
return output;
}

// We already lowercased the first element, so start at one. Look at the
// current element and the next one. To handle cases like UTF8String, start
// making all the uppercase characters lower, until we see an upper case
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,22 @@ class PrivatePropertyWithSameName {
void setValue(int i);
};

struct SnakeCaseGetterSetter {
int value = 42;
int get_foo() const { return value; };
void set_foo(int v) { value = v; };
};

struct SnakeCaseUTF8Str {
int value = 42;
int get_utf8_string() const { return value; };
void set_utf8_string(int v) { value = v; };
};

struct SnakeCaseTrailing {
int value = 42;
int get_x_() const { return value; };
void set_x_(int v) { value = v; } ;
};

#endif // SWIFT_IMPLICIT_COMPUTED_PROPERTIES_H
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
// CHECK: struct IntGetterSetterSnakeCase {
// CHECK-NEXT: init()
// CHECK-NEXT: init(val: Int32)
// CHECK-NEXT: var x: Int32
// CHECK-NEXT: func get_x() -> Int32
// CHECK-NEXT: mutating func set_x(_ v: Int32)
// CHECK-NEXT: var val: Int32
Expand Down Expand Up @@ -266,3 +267,30 @@
// CHECK-NEXT: func getValue() -> Int32
// CHECK-NEXT: mutating func setValue(_ i: Int32)
// CHECK-NEXT: }

// CHECK: struct SnakeCaseGetterSetter {
// CHECK-NEXT: init()
// CHECK-NEXT: init(value: Int32)
// CHECK-NEXT: var foo: Int32
// CHECK-NEXT: func get_foo() -> Int32
// CHECK-NEXT: mutating func set_foo(_ v: Int32)
// CHECK-NEXT: var value: Int32
// CHECK-NEXT: }

// CHECK: struct SnakeCaseUTF8Str {
// CHECK-NEXT: init()
// CHECK-NEXT: init(value: Int32)
// CHECK-NEXT: var utf8String: Int32
// CHECK-NEXT: func get_utf8_string() -> Int32
// CHECK-NEXT: mutating func set_utf8_string(_ v: Int32)
// CHECK-NEXT: var value: Int32
// CHECK-NEXT: }

// CHECK: struct SnakeCaseTrailing {
// CHECK-NEXT: init()
// CHECK-NEXT: init(value: Int32)
// CHECK-NEXT: var x: Int32
// CHECK-NEXT: func get_x_() -> Int32
// CHECK-NEXT: mutating func set_x_(_ v: Int32)
// CHECK-NEXT: var value: Int32
// CHECK-NEXT: }
16 changes: 15 additions & 1 deletion test/Interop/Cxx/ergonomics/implicit-computed-properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ImplicitComputedPropertiesTestSuite.test("UpperCaseMix") {
}

ImplicitComputedPropertiesTestSuite.test("GetterOnly") {
var d = GetterOnly()
let d = GetterOnly()
expectEqual(d.foo, 42)
}

Expand Down Expand Up @@ -79,4 +79,18 @@ ImplicitComputedPropertiesTestSuite.test("non trivial") {
expectEqual(Object.x.value, 20)
}

ImplicitComputedPropertiesTestSuite.test("SnakeCaseGetterSetter") {
var object = SnakeCaseGetterSetter()
expectEqual(object.foo, 42)
object.foo = 32
expectEqual(object.foo, 32)
}

ImplicitComputedPropertiesTestSuite.test("SnakeCaseUTF8Str") {
var object = SnakeCaseUTF8Str()
expectEqual(object.utf8String, 42)
object.utf8String = 32
expectEqual(object.utf8String, 32)
}

runAllTests()
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ bb0(%arg0 : @owned $MyKlass, %arg1 : @owned $MyKlass, %arg2 : @owned $MyKlass):
return %52 : $()
}

sil [ossa] @unkown_use : $@convention(thin) (@owned MyKlass) -> () {
sil [ossa] @unknown_use : $@convention(thin) (@owned MyKlass) -> () {
bb0(%arg0 : @owned $MyKlass):
%0 = function_ref @swift_bufferAllocate : $@convention(thin) () -> @owned _ContiguousArrayStorage<MyKlass>
%1 = integer_literal $Builtin.Word, 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def test(self):
[option.option_string])
# The argument should never show up in the namespace
self.assertFalse(hasattr(namespace, option.dest))
# It should instead be forwareded to unkown_args
# It should instead be forwareded to unknown_args
self.assertEqual(unknown_args, [option.option_string])

return test
Expand Down