Pārlūkot izejas kodu

Merge pull request #100024 from Ivorforce/optimize-string-single-char

Optimize string single char contains calls.
Thaddeus Crews 9 mēneši atpakaļ
vecāks
revīzija
a607bca2fd
61 mainītis faili ar 108 papildinājumiem un 99 dzēšanām
  1. 1 1
      core/debugger/remote_debugger_peer.cpp
  2. 1 1
      core/extension/extension_api_dump.cpp
  3. 1 1
      core/io/file_access.cpp
  4. 2 2
      core/io/file_access_pack.cpp
  5. 1 1
      core/io/ip_address.cpp
  6. 1 1
      core/object/class_db.cpp
  7. 9 1
      core/string/ustring.cpp
  8. 1 0
      core/string/ustring.h
  9. 1 1
      core/variant/variant_parser.cpp
  10. 2 2
      drivers/unix/os_unix.cpp
  11. 1 1
      editor/connections_dialog.cpp
  12. 1 1
      editor/debugger/debug_adapter/debug_adapter_parser.cpp
  13. 1 1
      editor/debugger/debug_adapter/debug_adapter_parser.h
  14. 3 3
      editor/directory_create_dialog.cpp
  15. 1 1
      editor/editor_feature_profile.cpp
  16. 1 1
      editor/editor_file_system.cpp
  17. 4 4
      editor/editor_help.cpp
  18. 3 3
      editor/editor_inspector.cpp
  19. 2 2
      editor/editor_properties.cpp
  20. 2 2
      editor/editor_sectioned_inspector.cpp
  21. 2 2
      editor/filesystem_dock.cpp
  22. 1 1
      editor/gui/scene_tree_editor.cpp
  23. 2 2
      editor/import/3d/collada.cpp
  24. 1 1
      editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
  25. 1 1
      editor/plugins/animation_blend_tree_editor_plugin.cpp
  26. 1 1
      editor/plugins/animation_library_editor.cpp
  27. 4 4
      editor/plugins/animation_player_editor_plugin.cpp
  28. 1 1
      editor/plugins/animation_state_machine_editor.cpp
  29. 1 1
      editor/plugins/resource_preloader_editor_plugin.cpp
  30. 1 1
      editor/plugins/script_editor_plugin.cpp
  31. 1 1
      editor/plugins/script_text_editor.cpp
  32. 1 1
      editor/project_converter_3_to_4.cpp
  33. 1 1
      editor/project_manager.cpp
  34. 2 2
      editor/project_manager/project_list.cpp
  35. 1 1
      editor/project_settings_editor.cpp
  36. 2 2
      editor/property_selector.cpp
  37. 1 1
      editor/script_create_dialog.cpp
  38. 1 1
      editor/shader_create_dialog.cpp
  39. 4 4
      main/main.cpp
  40. 1 1
      modules/gdscript/editor/gdscript_highlighter.cpp
  41. 7 7
      modules/gdscript/gdscript_editor.cpp
  42. 1 1
      modules/gdscript/gdscript_parser.cpp
  43. 1 1
      modules/gdscript/language_server/gdscript_extend_parser.cpp
  44. 2 2
      modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
  45. 1 1
      modules/gltf/gltf_document.cpp
  46. 3 3
      modules/mono/editor/bindings_generator.cpp
  47. 1 1
      modules/openxr/scene/openxr_composition_layer.cpp
  48. 1 1
      modules/text_server_adv/thorvg_svg_in_ot.cpp
  49. 1 1
      modules/text_server_fb/thorvg_svg_in_ot.cpp
  50. 2 2
      platform/windows/export/export_plugin.cpp
  51. 2 2
      platform/windows/os_windows.cpp
  52. 1 1
      scene/3d/skeleton_3d.cpp
  53. 1 1
      scene/animation/animation_blend_tree.cpp
  54. 2 2
      scene/animation/animation_mixer.cpp
  55. 3 3
      scene/animation/animation_node_state_machine.cpp
  56. 2 2
      scene/animation/animation_tree.cpp
  57. 2 2
      scene/resources/animation_library.cpp
  58. 2 2
      scene/resources/theme.cpp
  59. 1 1
      scene/resources/visual_shader.cpp
  60. 2 2
      servers/rendering/rendering_device_binds.cpp
  61. 1 1
      servers/rendering/shader_compiler.cpp

+ 1 - 1
core/debugger/remote_debugger_peer.cpp

@@ -223,7 +223,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
 	String debug_host = p_uri.replace("tcp://", "");
 	uint16_t debug_port = 6007;
 
-	if (debug_host.contains(":")) {
+	if (debug_host.contains_char(':')) {
 		int sep_pos = debug_host.rfind_char(':');
 		debug_port = debug_host.substr(sep_pos + 1).to_int();
 		debug_host = debug_host.substr(0, sep_pos);

+ 1 - 1
core/extension/extension_api_dump.cpp

@@ -1205,7 +1205,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
 					if (F.name.begins_with("_")) {
 						continue; //hidden property
 					}
-					if (F.name.contains("/")) {
+					if (F.name.contains_char('/')) {
 						// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
 						continue;
 					}

+ 1 - 1
core/io/file_access.cpp

@@ -740,7 +740,7 @@ bool FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
 	for (int i = 0; i < size; ++i) {
 		String value = p_values[i];
 
-		if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) {
+		if (value.contains_char('"') || value.contains(p_delim) || value.contains_char('\n')) {
 			value = "\"" + value.replace("\"", "\"\"") + "\"";
 		}
 		if (i < size - 1) {

+ 2 - 2
core/io/file_access_pack.cpp

@@ -71,7 +71,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
 		// Search for directory.
 		PackedDir *cd = root;
 
-		if (simplified_path.contains("/")) { // In a subdirectory.
+		if (simplified_path.contains_char('/')) { // In a subdirectory.
 			Vector<String> ds = simplified_path.get_base_dir().split("/");
 
 			for (int j = 0; j < ds.size(); j++) {
@@ -104,7 +104,7 @@ void PackedData::remove_path(const String &p_path) {
 	// Search for directory.
 	PackedDir *cd = root;
 
-	if (simplified_path.contains("/")) { // In a subdirectory.
+	if (simplified_path.contains_char('/')) { // In a subdirectory.
 		Vector<String> ds = simplified_path.get_base_dir().split("/");
 
 		for (int j = 0; j < ds.size(); j++) {

+ 1 - 1
core/io/ip_address.cpp

@@ -202,7 +202,7 @@ IPAddress::IPAddress(const String &p_string) {
 		// Wildcard (not a valid IP)
 		wildcard = true;
 
-	} else if (p_string.contains(":")) {
+	} else if (p_string.contains_char(':')) {
 		// IPv6
 		_parse_ipv6(p_string);
 		valid = true;

+ 1 - 1
core/object/class_db.cpp

@@ -1116,7 +1116,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
 
 	String enum_name = p_enum;
 	if (!enum_name.is_empty()) {
-		if (enum_name.contains(".")) {
+		if (enum_name.contains_char('.')) {
 			enum_name = enum_name.get_slicec('.', 1);
 		}
 

+ 9 - 1
core/string/ustring.cpp

@@ -3314,6 +3314,10 @@ int String::find(const String &p_str, int p_from) const {
 		return -1; // won't find anything!
 	}
 
+	if (src_len == 1) {
+		return find_char(p_str[0], p_from); // Optimize with single-char find.
+	}
+
 	const char32_t *src = get_data();
 	const char32_t *str = p_str.get_data();
 
@@ -3354,6 +3358,10 @@ int String::find(const char *p_str, int p_from) const {
 		return -1; // won't find anything!
 	}
 
+	if (src_len == 1) {
+		return find_char(*p_str, p_from); // Optimize with single-char find.
+	}
+
 	const char32_t *src = get_data();
 
 	if (src_len == 1) {
@@ -4081,7 +4089,7 @@ String String::format(const Variant &values, const String &placeholder) const {
 				Variant v_val = values_arr[i];
 				String val = v_val;
 
-				if (placeholder.contains("_")) {
+				if (placeholder.contains_char('_')) {
 					new_string = new_string.replace(placeholder.replace("_", i_as_str), val);
 				} else {
 					new_string = new_string.replace_first(placeholder, val);

+ 1 - 0
core/string/ustring.h

@@ -432,6 +432,7 @@ public:
 	_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
 	_FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; }
 	_FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; }
+	_FORCE_INLINE_ bool contains_char(char32_t p_chr) const { return find_char(p_chr) != -1; }
 	_FORCE_INLINE_ bool containsn(const char *p_str) const { return findn(p_str) != -1; }
 	_FORCE_INLINE_ bool containsn(const String &p_str) const { return findn(p_str) != -1; }
 

+ 1 - 1
core/variant/variant_parser.cpp

@@ -1961,7 +1961,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 		case Variant::FLOAT: {
 			String s = rtos_fix(p_variant.operator double());
 			if (s != "inf" && s != "inf_neg" && s != "nan") {
-				if (!s.contains(".") && !s.contains("e")) {
+				if (!s.contains_char('.') && !s.contains_char('e')) {
 					s += ".0";
 				}
 			}

+ 2 - 2
drivers/unix/os_unix.cpp

@@ -949,13 +949,13 @@ String OS_Unix::get_environment(const String &p_var) const {
 }
 
 void OS_Unix::set_environment(const String &p_var, const String &p_value) const {
-	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
 	int err = setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ 1);
 	ERR_FAIL_COND_MSG(err != 0, vformat("Failed setting environment variable '%s', the system is out of memory.", p_var));
 }
 
 void OS_Unix::unset_environment(const String &p_var) const {
-	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
 	unsetenv(p_var.utf8().get_data());
 }
 

+ 1 - 1
editor/connections_dialog.cpp

@@ -523,7 +523,7 @@ void ConnectDialog::set_dst_node(Node *p_node) {
 
 StringName ConnectDialog::get_dst_method_name() const {
 	String txt = dst_method->get_text();
-	if (txt.contains("(")) {
+	if (txt.contains_char('(')) {
 		txt = txt.left(txt.find_char('(')).strip_edges();
 	}
 	return txt;

+ 1 - 1
editor/debugger/debug_adapter/debug_adapter_parser.cpp

@@ -359,7 +359,7 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) co
 	}
 
 	// If path contains \, it's a Windows path, so we need to convert it to /, and make the drive letter uppercase
-	if (source.path.contains("\\")) {
+	if (source.path.contains_char('\\')) {
 		source.path = source.path.replace("\\", "/");
 		source.path = source.path.substr(0, 1).to_upper() + source.path.substr(1);
 	}

+ 1 - 1
editor/debugger/debug_adapter/debug_adapter_parser.h

@@ -47,7 +47,7 @@ private:
 
 	_FORCE_INLINE_ bool is_valid_path(const String &p_path) const {
 		// If path contains \, it's a Windows path, so we need to convert it to /, and check as case-insensitive.
-		if (p_path.contains("\\")) {
+		if (p_path.contains_char('\\')) {
 			String project_path = ProjectSettings::get_singleton()->get_resource_path();
 			String path = p_path.replace("\\", "/");
 			return path.containsn(project_path);

+ 3 - 3
editor/directory_create_dialog.cpp

@@ -67,8 +67,8 @@ String DirectoryCreateDialog::_validate_path(const String &p_path) const {
 				return TTR("Folder name cannot be empty.");
 			}
 		}
-		if (part.contains("\\") || part.contains(":") || part.contains("*") ||
-				part.contains("|") || part.contains(">") || part.ends_with(".") || part.ends_with(" ")) {
+		if (part.contains_char('\\') || part.contains_char(':') || part.contains_char('*') ||
+				part.contains_char('|') || part.contains_char('>') || part.ends_with(".") || part.ends_with(" ")) {
 			if (is_file) {
 				return TTR("File name contains invalid characters.");
 			} else {
@@ -101,7 +101,7 @@ void DirectoryCreateDialog::_on_dir_path_changed() {
 	const String error = _validate_path(path);
 
 	if (error.is_empty()) {
-		if (path.contains("/")) {
+		if (path.contains_char('/')) {
 			if (mode == MODE_DIRECTORY) {
 				validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK);
 			} else {

+ 1 - 1
editor/editor_feature_profile.cpp

@@ -472,7 +472,7 @@ void EditorFeatureProfileManager::_erase_selected_profile() {
 
 void EditorFeatureProfileManager::_create_new_profile() {
 	String name = new_profile_name->get_text().strip_edges();
-	if (!name.is_valid_filename() || name.contains(".")) {
+	if (!name.is_valid_filename() || name.contains_char('.')) {
 		EditorNode::get_singleton()->show_warning(TTR("Profile must be a valid filename and must not contain '.'"));
 		return;
 	}

+ 1 - 1
editor/editor_file_system.cpp

@@ -384,7 +384,7 @@ void EditorFileSystem::_scan_filesystem() {
 
 					FileCache fc;
 					fc.type = split[1];
-					if (fc.type.contains("/")) {
+					if (fc.type.contains_char('/')) {
 						fc.type = split[1].get_slice("/", 0);
 						fc.resource_script_class = split[1].get_slice("/", 1);
 					}

+ 4 - 4
editor/editor_help.cpp

@@ -320,7 +320,7 @@ void EditorHelp::_class_desc_select(const String &p_select) {
 				}
 			}
 
-			if (link.contains(".")) {
+			if (link.contains_char('.')) {
 				const int class_end = link.find_char('.');
 				emit_signal(SNAME("go_to_help"), topic + ":" + link.left(class_end) + ":" + link.substr(class_end + 1));
 			}
@@ -365,7 +365,7 @@ static void _add_type_to_rt(const String &p_type, const String &p_enum, bool p_i
 
 	bool is_enum_type = !p_enum.is_empty();
 	bool is_bitfield = p_is_bitfield && is_enum_type;
-	bool can_ref = !p_type.contains("*") || is_enum_type;
+	bool can_ref = !p_type.contains_char('*') || is_enum_type;
 
 	String link_t = p_type; // For links in metadata
 	String display_t; // For display purposes.
@@ -2552,7 +2552,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C
 			p_rt->push_meta("@" + link_tag + " " + link_target, underline_mode);
 
 			if (link_tag == "member" &&
-					((!link_target.contains(".") && (p_class == "ProjectSettings" || p_class == "EditorSettings")) ||
+					((!link_target.contains_char('.') && (p_class == "ProjectSettings" || p_class == "EditorSettings")) ||
 							link_target.begins_with("ProjectSettings.") || link_target.begins_with("EditorSettings."))) {
 				// Special formatting for both ProjectSettings and EditorSettings.
 				String prefix;
@@ -3665,7 +3665,7 @@ void EditorHelpBit::_meta_clicked(const String &p_select) {
 			return;
 		}
 
-		if (link.contains(".")) {
+		if (link.contains_char('.')) {
 			const int class_end = link.find_char('.');
 			_go_to_help(topic + ":" + link.left(class_end) + ":" + link.substr(class_end + 1));
 		} else {

+ 3 - 3
editor/editor_inspector.cpp

@@ -281,7 +281,7 @@ void EditorProperty::_notification(int p_what) {
 			} else {
 				color = get_theme_color(is_read_only() ? SNAME("readonly_color") : SNAME("property_color"));
 			}
-			if (label.contains(".")) {
+			if (label.contains_char('.')) {
 				// FIXME: Move this to the project settings editor, as this is only used
 				// for project settings feature tag overrides.
 				color.a = 0.5;
@@ -3177,7 +3177,7 @@ void EditorInspector::update_tree() {
 		}
 
 		// Get the property label's string.
-		String name_override = (path.contains("/")) ? path.substr(path.rfind_char('/') + 1) : path;
+		String name_override = (path.contains_char('/')) ? path.substr(path.rfind_char('/') + 1) : path;
 		String feature_tag;
 		{
 			const int dot = name_override.find_char('.');
@@ -3326,7 +3326,7 @@ void EditorInspector::update_tree() {
 				array_element_prefix = class_name_components[0];
 				editor_inspector_array = memnew(EditorInspectorArray(all_read_only));
 
-				String array_label = path.contains("/") ? path.substr(path.rfind_char('/') + 1) : path;
+				String array_label = path.contains_char('/') ? path.substr(path.rfind_char('/') + 1) : path;
 				array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style, p.name, doc_name);
 				int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0;
 				editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding);

+ 2 - 2
editor/editor_properties.cpp

@@ -837,7 +837,7 @@ void EditorPropertyLayersGrid::_rename_operation_confirm() {
 	if (new_name.length() == 0) {
 		EditorNode::get_singleton()->show_warning(TTR("No name provided."));
 		return;
-	} else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
+	} else if (new_name.contains_char('/') || new_name.contains_char('\\') || new_name.contains_char(':')) {
 		EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
 		return;
 	}
@@ -2873,7 +2873,7 @@ void EditorPropertyNodePath::update_property() {
 	const Node *target_node = base_node->get_node(p);
 	ERR_FAIL_NULL(target_node);
 
-	if (String(target_node->get_name()).contains("@")) {
+	if (String(target_node->get_name()).contains_char('@')) {
 		assign->set_button_icon(Ref<Texture2D>());
 		assign->set_text(p);
 		return;

+ 2 - 2
editor/editor_sectioned_inspector.cpp

@@ -108,7 +108,7 @@ class SectionedInspectorFilter : public Object {
 
 			if (pi.name.begins_with(section + "/")) {
 				pi.name = pi.name.replace_first(section + "/", "");
-				if (!allow_sub && pi.name.contains("/")) {
+				if (!allow_sub && pi.name.contains_char('/')) {
 					continue;
 				}
 				p_list->push_back(pi);
@@ -247,7 +247,7 @@ void SectionedInspector::update_category_list() {
 			continue;
 		}
 
-		if (pi.name.contains(":") || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene" || pi.name.begins_with("_global_script")) {
+		if (pi.name.contains_char(':') || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene" || pi.name.begins_with("_global_script")) {
 			continue;
 		}
 

+ 2 - 2
editor/filesystem_dock.cpp

@@ -1803,7 +1803,7 @@ void FileSystemDock::_rename_operation_confirm() {
 	if (new_name.length() == 0) {
 		EditorNode::get_singleton()->show_warning(TTR("No name provided."));
 		rename_error = true;
-	} else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
+	} else if (new_name.contains_char('/') || new_name.contains_char('\\') || new_name.contains_char(':')) {
 		EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
 		rename_error = true;
 	} else if (new_name[0] == '.') {
@@ -2266,7 +2266,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
 				test_args.push_back("command -v " + terminal_emulator);
 				const Error err = OS::get_singleton()->execute("bash", test_args, &pipe);
 				// Check if a path to the terminal executable exists.
-				if (err == OK && pipe.contains("/")) {
+				if (err == OK && pipe.contains_char('/')) {
 					chosen_terminal_emulator = terminal_emulator;
 					break;
 				} else if (err == ERR_CANT_FORK) {

+ 1 - 1
editor/gui/scene_tree_editor.cpp

@@ -759,7 +759,7 @@ bool SceneTreeEditor::_item_matches_all_terms(TreeItem *p_item, const PackedStri
 		const String &term = p_terms[i];
 
 		// Recognize special filter.
-		if (term.contains(":") && !term.get_slicec(':', 0).is_empty()) {
+		if (term.contains_char(':') && !term.get_slicec(':', 0).is_empty()) {
 			String parameter = term.get_slicec(':', 0);
 			String argument = term.get_slicec(':', 1);
 

+ 2 - 2
editor/import/3d/collada.cpp

@@ -1808,10 +1808,10 @@ void Collada::_parse_animation(XMLParser &p_parser) {
 				}
 			}
 
-			if (target.contains("/")) { //transform component
+			if (target.contains_char('/')) { //transform component
 				track.target = target.get_slicec('/', 0);
 				track.param = target.get_slicec('/', 1);
-				if (track.param.contains(".")) {
+				if (track.param.contains_char('.')) {
 					track.component = track.param.get_slice(".", 1).to_upper();
 				}
 				track.param = track.param.get_slice(".", 0);

+ 1 - 1
editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp

@@ -239,7 +239,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
 				List<StringName> anims;
 				ap->get_animation_list(&anims);
 				for (const StringName &name : anims) {
-					if (String(name).contains("/")) {
+					if (String(name).contains_char('/')) {
 						continue; // Avoid animation library which may be created by importer dynamically.
 					}
 

+ 1 - 1
editor/plugins/animation_blend_tree_editor_plugin.cpp

@@ -1068,7 +1068,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
 
 	const String &new_name = p_text;
 
-	ERR_FAIL_COND(new_name.is_empty() || new_name.contains(".") || new_name.contains("/"));
+	ERR_FAIL_COND(new_name.is_empty() || new_name.contains_char('.') || new_name.contains_char('/'));
 
 	if (new_name == prev_name) {
 		return; //nothing to do

+ 1 - 1
editor/plugins/animation_library_editor.cpp

@@ -470,7 +470,7 @@ void AnimationLibraryEditor::_item_renamed() {
 	bool restore_text = false;
 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
 
-	if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) {
+	if (String(text).contains_char('/') || String(text).contains_char(':') || String(text).contains_char(',') || String(text).contains_char('[')) {
 		restore_text = true;
 	} else {
 		if (ti->get_parent() == tree->get_root()) {

+ 4 - 4
editor/plugins/animation_player_editor_plugin.cpp

@@ -504,7 +504,7 @@ void AnimationPlayerEditor::_animation_rename() {
 	String selected_name = animation->get_item_text(selected);
 
 	// Remove library prefix if present.
-	if (selected_name.contains("/")) {
+	if (selected_name.contains_char('/')) {
 		selected_name = selected_name.get_slice("/", 1);
 	}
 
@@ -537,7 +537,7 @@ void AnimationPlayerEditor::_animation_remove_confirmed() {
 	ERR_FAIL_COND(al.is_null());
 
 	// For names of form lib_name/anim_name, remove library name prefix.
-	if (current.contains("/")) {
+	if (current.contains_char('/')) {
 		current = current.get_slice("/", 1);
 	}
 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
@@ -626,7 +626,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
 
 			// Extract library prefix if present.
 			String new_library_prefix = "";
-			if (current.contains("/")) {
+			if (current.contains_char('/')) {
 				new_library_prefix = current.get_slice("/", 0) + "/";
 				current = current.get_slice("/", 1);
 			}
@@ -1340,7 +1340,7 @@ void AnimationPlayerEditor::_animation_duplicate() {
 		break;
 	}
 
-	if (new_name.contains("/")) {
+	if (new_name.contains_char('/')) {
 		// Discard library prefix.
 		new_name = new_name.get_slice("/", 1);
 	}

+ 1 - 1
editor/plugins/animation_state_machine_editor.cpp

@@ -1618,7 +1618,7 @@ void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) {
 void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
 	const String &new_name = p_text;
 
-	ERR_FAIL_COND(new_name.is_empty() || new_name.contains(".") || new_name.contains("/"));
+	ERR_FAIL_COND(new_name.is_empty() || new_name.contains_char('.') || new_name.contains_char('/'));
 
 	if (new_name == prev_name) {
 		return; // Nothing to do.

+ 1 - 1
editor/plugins/resource_preloader_editor_plugin.cpp

@@ -113,7 +113,7 @@ void ResourcePreloaderEditor::_item_edited() {
 			return;
 		}
 
-		if (new_name.is_empty() || new_name.contains("\\") || new_name.contains("/") || preloader->has_resource(new_name)) {
+		if (new_name.is_empty() || new_name.contains_char('\\') || new_name.contains_char('/') || preloader->has_resource(new_name)) {
 			s->set_text(0, old_name);
 			return;
 		}

+ 1 - 1
editor/plugins/script_editor_plugin.cpp

@@ -183,7 +183,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
 				if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
 					continue;
 				}
-				if (prop_name.contains("/")) {
+				if (prop_name.contains_char('/')) {
 					continue;
 				}
 				highlighter->add_member_keyword_color(prop_name, member_variable_color);

+ 1 - 1
editor/plugins/script_text_editor.cpp

@@ -1636,7 +1636,7 @@ void ScriptTextEditor::_edit_option_toggle_inline_comment() {
 	script->get_language()->get_comment_delimiters(&comment_delimiters);
 
 	for (const String &script_delimiter : comment_delimiters) {
-		if (!script_delimiter.contains(" ")) {
+		if (!script_delimiter.contains_char(' ')) {
 			delimiter = script_delimiter;
 			break;
 		}

+ 1 - 1
editor/project_converter_3_to_4.cpp

@@ -1677,7 +1677,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
 	}
 
 	// -- \t.func() -> \tsuper.func()       Object
-	if (line.contains("(") && line.contains(".")) {
+	if (line.contains_char('(') && line.contains_char('.')) {
 		line = reg_container.reg_super.sub(line, "$1super.$2", true); // TODO, not sure if possible, but for now this broke String text e.g. "Chosen .gitignore" -> "Chosen super.gitignore"
 	}
 

+ 1 - 1
editor/project_manager.cpp

@@ -834,7 +834,7 @@ void ProjectManager::_set_new_tag_name(const String p_name) {
 		return;
 	}
 
-	if (p_name.contains(" ")) {
+	if (p_name.contains_char(' ')) {
 		tag_error->set_text(TTR("Tag name can't contain spaces."));
 		return;
 	}

+ 2 - 2
editor/project_manager/project_list.cpp

@@ -350,7 +350,7 @@ const char *ProjectList::SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
 // Helpers.
 
 bool ProjectList::project_feature_looks_like_version(const String &p_feature) {
-	return p_feature.contains(".") && p_feature.substr(0, 3).is_numeric();
+	return p_feature.contains_char('.') && p_feature.substr(0, 3).is_numeric();
 }
 
 // Notifications.
@@ -581,7 +581,7 @@ void ProjectList::sort_projects() {
 		bool item_visible = true;
 		if (!_search_term.is_empty()) {
 			String search_path;
-			if (search_term.contains("/")) {
+			if (search_term.contains_char('/')) {
 				// Search path will match the whole path
 				search_path = item.path;
 			} else {

+ 1 - 1
editor/project_settings_editor.cpp

@@ -268,7 +268,7 @@ void ProjectSettingsEditor::shortcut_input(const Ref<InputEvent> &p_event) {
 
 String ProjectSettingsEditor::_get_setting_name() const {
 	String name = property_box->get_text().strip_edges();
-	if (!name.begins_with("_") && !name.contains("/")) {
+	if (!name.begins_with("_") && !name.contains_char('/')) {
 		name = "global/" + name;
 	}
 	return name;

+ 2 - 2
editor/property_selector.cpp

@@ -258,7 +258,7 @@ void PropertySelector::_update_search() {
 			TreeItem *item = search_options->create_item(category ? category : root);
 
 			String desc;
-			if (mi.name.contains(":")) {
+			if (mi.name.contains_char(':')) {
 				desc = mi.name.get_slice(":", 1) + " ";
 				mi.name = mi.name.get_slice(":", 0);
 			} else if (mi.return_val.type != Variant::NIL) {
@@ -278,7 +278,7 @@ void PropertySelector::_update_search() {
 
 				if (arg_itr->type == Variant::NIL) {
 					desc += ": Variant";
-				} else if (arg_itr->name.contains(":")) {
+				} else if (arg_itr->name.contains_char(':')) {
 					desc += vformat(": %s", arg_itr->name.get_slice(":", 1));
 					arg_itr->name = arg_itr->name.get_slice(":", 0);
 				} else {

+ 1 - 1
editor/script_create_dialog.cpp

@@ -744,7 +744,7 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL
 	List<String> comment_delimiters;
 	p_language->get_comment_delimiters(&comment_delimiters);
 	for (const String &script_delimiter : comment_delimiters) {
-		if (!script_delimiter.contains(" ")) {
+		if (!script_delimiter.contains_char(' ')) {
 			meta_delimiter = script_delimiter;
 			break;
 		}

+ 1 - 1
editor/shader_create_dialog.cpp

@@ -292,7 +292,7 @@ void ShaderCreateDialog::_type_changed(int p_language) {
 	String extension = "";
 
 	if (!path.is_empty()) {
-		if (path.contains(".")) {
+		if (path.contains_char('.')) {
 			extension = path.get_extension();
 		}
 		if (extension.length() == 0) {

+ 4 - 4
main/main.cpp

@@ -1290,7 +1290,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			if (N) {
 				String vm = N->get();
 
-				if (!vm.contains("x")) { // invalid parameter format
+				if (!vm.contains_char('x')) { // invalid parameter format
 
 					OS::get_singleton()->print("Invalid resolution '%s', it should be e.g. '1280x720'.\n",
 							vm.utf8().get_data());
@@ -1333,7 +1333,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			if (N) {
 				String vm = N->get();
 
-				if (!vm.contains(",")) { // invalid parameter format
+				if (!vm.contains_char(',')) { // invalid parameter format
 
 					OS::get_singleton()->print("Invalid position '%s', it should be e.g. '80,128'.\n",
 							vm.utf8().get_data());
@@ -1832,7 +1832,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	// 'project.godot' file which will only be available through the network if this is enabled
 	if (!remotefs.is_empty()) {
 		int port;
-		if (remotefs.contains(":")) {
+		if (remotefs.contains_char(':')) {
 			port = remotefs.get_slicec(':', 1).to_int();
 			remotefs = remotefs.get_slicec(':', 0);
 		} else {
@@ -3275,7 +3275,7 @@ Error Main::setup2(bool p_show_boot_logo) {
 				// Dummy text driver cannot draw any text, making the editor unusable if selected.
 				continue;
 			}
-			if (!text_driver_options.is_empty() && !text_driver_options.contains(",")) {
+			if (!text_driver_options.is_empty() && !text_driver_options.contains_char(',')) {
 				// Not the first option; add a comma before it as a separator for the property hint.
 				text_driver_options += ",";
 			}

+ 1 - 1
modules/gdscript/editor/gdscript_highlighter.cpp

@@ -815,7 +815,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
 				if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
 					continue;
 				}
-				if (prop_name.contains("/")) {
+				if (prop_name.contains_char('/')) {
 					continue;
 				}
 				member_keywords[prop_name] = member_variable_color;

+ 7 - 7
modules/gdscript/gdscript_editor.cpp

@@ -1210,7 +1210,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
 								if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {
 									continue;
 								}
-								if (E.name.contains("/")) {
+								if (E.name.contains_char('/')) {
 									continue;
 								}
 								int location = p_recursion_depth + _get_property_location(scr, E.name);
@@ -1301,7 +1301,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
 							if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {
 								continue;
 							}
-							if (E.name.contains("/")) {
+							if (E.name.contains_char('/')) {
 								continue;
 							}
 							int location = p_recursion_depth + _get_property_location(type, E.name);
@@ -1383,7 +1383,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
 						if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {
 							continue;
 						}
-						if (!String(E.name).contains("/")) {
+						if (!String(E.name).contains_char('/')) {
 							ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
 							if (base_type.kind == GDScriptParser::DataType::ENUM) {
 								// Sort enum members in their declaration order.
@@ -2719,7 +2719,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
 }
 
 static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
-	if (!p_enum_hint.contains(".")) {
+	if (!p_enum_hint.contains_char('.')) {
 		// Global constant or in the current class.
 		StringName current_enum = p_enum_hint;
 		if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) {
@@ -3474,7 +3474,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
 
 			for (const MethodInfo &mi : virtual_methods) {
 				String method_hint = mi.name;
-				if (method_hint.contains(":")) {
+				if (method_hint.contains_char(':')) {
 					method_hint = method_hint.get_slice(":", 0);
 				}
 				method_hint += "(";
@@ -3484,7 +3484,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
 						method_hint += ", ";
 					}
 					String arg = arg_itr->name;
-					if (arg.contains(":")) {
+					if (arg.contains_char(':')) {
 						arg = arg.substr(0, arg.find_char(':'));
 					}
 					method_hint += arg;
@@ -3542,7 +3542,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
 					if (path_needs_quote) {
 						// Ignore quote_style and just use double quotes for paths with apostrophes.
 						// Double quotes don't need to be checked because they're not valid in node and property names.
-						opt = opt.quote(opt.contains("'") ? "\"" : quote_style); // Handle user preference.
+						opt = opt.quote(opt.contains_char('\'') ? "\"" : quote_style); // Handle user preference.
 					}
 					ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
 					options.insert(option.display, option);

+ 1 - 1
modules/gdscript/gdscript_parser.cpp

@@ -4323,7 +4323,7 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta
 				push_error(vformat(R"(Argument %d of annotation "%s" is empty.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
 				return false;
 			}
-			if (arg_string.contains(",")) {
+			if (arg_string.contains_char(',')) {
 				push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
 				return false;
 			}

+ 1 - 1
modules/gdscript/language_server/gdscript_extend_parser.cpp

@@ -67,7 +67,7 @@ lsp::Position GodotPosition::to_lsp(const Vector<String> &p_lines) const {
 	res.character = column - 1;
 
 	String pos_line = p_lines[res.line];
-	if (pos_line.contains("\t")) {
+	if (pos_line.contains_char('\t')) {
 		int tab_size = get_indent_size();
 
 		int in_col = 1;

+ 2 - 2
modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp

@@ -34,7 +34,7 @@ const uint32_t PROP_EDITOR_SCRIPT_VAR = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_S
 
 bool EditorSceneExporterGLTFSettings::_set(const StringName &p_name, const Variant &p_value) {
 	String name_str = String(p_name);
-	if (name_str.contains("/")) {
+	if (name_str.contains_char('/')) {
 		return _set_extension_setting(name_str, p_value);
 	}
 	if (p_name == StringName("image_format")) {
@@ -55,7 +55,7 @@ bool EditorSceneExporterGLTFSettings::_set(const StringName &p_name, const Varia
 
 bool EditorSceneExporterGLTFSettings::_get(const StringName &p_name, Variant &r_ret) const {
 	String name_str = String(p_name);
-	if (name_str.contains("/")) {
+	if (name_str.contains_char('/')) {
 		return _get_extension_setting(name_str, r_ret);
 	}
 	if (p_name == StringName("image_format")) {

+ 1 - 1
modules/gltf/gltf_document.cpp

@@ -4078,7 +4078,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
 			if (uri.begins_with("data:")) { // Embedded data using base64.
 				data = _parse_base64_uri(uri);
 				// mimeType is optional, but if we have it defined in the URI, let's use it.
-				if (mime_type.is_empty() && uri.contains(";")) {
+				if (mime_type.is_empty() && uri.contains_char(';')) {
 					// Trim "data:" prefix which is 5 characters long, and end at ";base64".
 					mime_type = uri.substr(5, uri.find(";base64") - 5);
 				}

+ 3 - 3
modules/mono/editor/bindings_generator.cpp

@@ -877,7 +877,7 @@ void BindingsGenerator::_append_text_method(StringBuilder &p_output, const TypeI
 }
 
 void BindingsGenerator::_append_text_member(StringBuilder &p_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
-	if (p_link_target.contains("/")) {
+	if (p_link_target.contains_char('/')) {
 		// Properties with '/' (slash) in the name are not declared in C#, so there is nothing to reference.
 		_append_text_undeclared(p_output, p_link_target);
 	} else if (!p_target_itype || !p_target_itype->is_object_type) {
@@ -1158,7 +1158,7 @@ void BindingsGenerator::_append_xml_method(StringBuilder &p_xml_output, const Ty
 }
 
 void BindingsGenerator::_append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
-	if (p_link_target.contains("/")) {
+	if (p_link_target.contains_char('/')) {
 		// Properties with '/' (slash) in the name are not declared in C#, so there is nothing to reference.
 		_append_xml_undeclared(p_xml_output, p_link_target);
 	} else if (!p_target_itype || !p_target_itype->is_object_type) {
@@ -3870,7 +3870,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 				continue;
 			}
 
-			if (property.name.contains("/")) {
+			if (property.name.contains_char('/')) {
 				// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
 				continue;
 			}

+ 1 - 1
modules/openxr/scene/openxr_composition_layer.cpp

@@ -443,7 +443,7 @@ void OpenXRCompositionLayer::_get_property_list(List<PropertyInfo> *p_property_l
 
 	for (const PropertyInfo &pinfo : extension_properties) {
 		StringName prop_name = pinfo.name;
-		if (!String(prop_name).contains("/")) {
+		if (!String(prop_name).contains_char('/')) {
 			WARN_PRINT_ONCE(vformat("Discarding OpenXRCompositionLayer property name '%s' from extension because it doesn't contain a '/'."));
 			continue;
 		}

+ 1 - 1
modules/text_server_adv/thorvg_svg_in_ot.cpp

@@ -97,7 +97,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
 			if (parser->has_attribute("id")) {
 				const String &gl_name = parser->get_named_attribute_value("id");
 				if (gl_name.begins_with("glyph")) {
-					int dot_pos = gl_name.find(".");
+					int dot_pos = gl_name.find_char('.');
 					int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
 					if (p_slot->glyph_index != gl_idx) {
 						parser->skip_section();

+ 1 - 1
modules/text_server_fb/thorvg_svg_in_ot.cpp

@@ -97,7 +97,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
 			if (parser->has_attribute("id")) {
 				const String &gl_name = parser->get_named_attribute_value("id");
 				if (gl_name.begins_with("glyph")) {
-					int dot_pos = gl_name.find(".");
+					int dot_pos = gl_name.find_char('.');
 					int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
 					if (p_slot->glyph_index != gl_idx) {
 						parser->skip_section();

+ 2 - 2
platform/windows/export/export_plugin.cpp

@@ -349,7 +349,7 @@ String EditorExportPlatformWindows::get_export_option_warning(const EditorExport
 				PackedStringArray version_array = file_version.split(".", false);
 				if (version_array.size() != 4 || !version_array[0].is_valid_int() ||
 						!version_array[1].is_valid_int() || !version_array[2].is_valid_int() ||
-						!version_array[3].is_valid_int() || file_version.contains("-")) {
+						!version_array[3].is_valid_int() || file_version.contains_char('-')) {
 					return TTR("Invalid file version.");
 				}
 			}
@@ -359,7 +359,7 @@ String EditorExportPlatformWindows::get_export_option_warning(const EditorExport
 				PackedStringArray version_array = product_version.split(".", false);
 				if (version_array.size() != 4 || !version_array[0].is_valid_int() ||
 						!version_array[1].is_valid_int() || !version_array[2].is_valid_int() ||
-						!version_array[3].is_valid_int() || product_version.contains("-")) {
+						!version_array[3].is_valid_int() || product_version.contains_char('-')) {
 					return TTR("Invalid product version.");
 				}
 			}

+ 2 - 2
platform/windows/os_windows.cpp

@@ -1727,7 +1727,7 @@ String OS_Windows::get_environment(const String &p_var) const {
 }
 
 void OS_Windows::set_environment(const String &p_var, const String &p_value) const {
-	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
 	Char16String var = p_var.utf16();
 	Char16String value = p_value.utf16();
 	ERR_FAIL_COND_MSG(var.length() + value.length() + 2 > 32767, vformat("Invalid definition for environment variable '%s', cannot exceed 32767 characters.", p_var));
@@ -1735,7 +1735,7 @@ void OS_Windows::set_environment(const String &p_var, const String &p_value) con
 }
 
 void OS_Windows::unset_environment(const String &p_var) const {
-	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+	ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
 	SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), nullptr); // Null to delete.
 }
 

+ 1 - 1
scene/3d/skeleton_3d.cpp

@@ -608,7 +608,7 @@ uint64_t Skeleton3D::get_version() const {
 }
 
 int Skeleton3D::add_bone(const String &p_name) {
-	ERR_FAIL_COND_V_MSG(p_name.is_empty() || p_name.contains(":") || p_name.contains("/"), -1, vformat("Bone name cannot be empty or contain ':' or '/'.", p_name));
+	ERR_FAIL_COND_V_MSG(p_name.is_empty() || p_name.contains_char(':') || p_name.contains_char('/'), -1, vformat("Bone name cannot be empty or contain ':' or '/'.", p_name));
 	ERR_FAIL_COND_V_MSG(name_to_bone_index.has(p_name), -1, vformat("Skeleton3D \"%s\" already has a bone with name \"%s\".", to_string(), p_name));
 
 	Bone b;

+ 1 - 1
scene/animation/animation_blend_tree.cpp

@@ -1437,7 +1437,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
 	ERR_FAIL_COND(nodes.has(p_name));
 	ERR_FAIL_COND(p_node.is_null());
 	ERR_FAIL_COND(p_name == SceneStringName(output));
-	ERR_FAIL_COND(String(p_name).contains("/"));
+	ERR_FAIL_COND(String(p_name).contains_char('/'));
 
 	Node n;
 	n.node = p_node;

+ 2 - 2
scene/animation/animation_mixer.cpp

@@ -294,7 +294,7 @@ StringName AnimationMixer::find_animation_library(const Ref<Animation> &p_animat
 Error AnimationMixer::add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library) {
 	ERR_FAIL_COND_V(p_animation_library.is_null(), ERR_INVALID_PARAMETER);
 #ifdef DEBUG_ENABLED
-	ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
+	ERR_FAIL_COND_V_MSG(String(p_name).contains_char('/') || String(p_name).contains_char(':') || String(p_name).contains_char(',') || String(p_name).contains_char('['), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
 #endif
 
 	int insert_pos = 0;
@@ -356,7 +356,7 @@ void AnimationMixer::rename_animation_library(const StringName &p_name, const St
 		return;
 	}
 #ifdef DEBUG_ENABLED
-	ERR_FAIL_COND_MSG(String(p_new_name).contains("/") || String(p_new_name).contains(":") || String(p_new_name).contains(",") || String(p_new_name).contains("["), "Invalid animation library name: " + String(p_new_name) + ".");
+	ERR_FAIL_COND_MSG(String(p_new_name).contains_char('/') || String(p_new_name).contains_char(':') || String(p_new_name).contains_char(',') || String(p_new_name).contains_char('['), "Invalid animation library name: " + String(p_new_name) + ".");
 #endif
 
 	bool found = false;

+ 3 - 3
scene/animation/animation_node_state_machine.cpp

@@ -54,7 +54,7 @@ AnimationNodeStateMachineTransition::AdvanceMode AnimationNodeStateMachineTransi
 
 void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) {
 	String cs = p_condition;
-	ERR_FAIL_COND(cs.contains("/") || cs.contains(":"));
+	ERR_FAIL_COND(cs.contains_char('/') || cs.contains_char(':'));
 	advance_condition = p_condition;
 	if (!cs.is_empty()) {
 		advance_condition_name = "conditions/" + cs;
@@ -1257,7 +1257,7 @@ bool AnimationNodeStateMachine::is_parameter_read_only(const StringName &p_param
 void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) {
 	ERR_FAIL_COND(states.has(p_name));
 	ERR_FAIL_COND(p_node.is_null());
-	ERR_FAIL_COND(String(p_name).contains("/"));
+	ERR_FAIL_COND(String(p_name).contains_char('/'));
 
 	State state_new;
 	state_new.node = p_node;
@@ -1276,7 +1276,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
 void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
 	ERR_FAIL_COND(states.has(p_name) == false);
 	ERR_FAIL_COND(p_node.is_null());
-	ERR_FAIL_COND(String(p_name).contains("/"));
+	ERR_FAIL_COND(String(p_name).contains_char('/'));
 
 	{
 		Ref<AnimationNode> node = states[p_name].node;

+ 2 - 2
scene/animation/animation_tree.cpp

@@ -325,7 +325,7 @@ bool AnimationNode::add_input(const String &p_name) {
 	// Root nodes can't add inputs.
 	ERR_FAIL_COND_V(Object::cast_to<AnimationRootNode>(this) != nullptr, false);
 	Input input;
-	ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false);
+	ERR_FAIL_COND_V(p_name.contains_char('.') || p_name.contains_char('/'), false);
 	input.name = p_name;
 	inputs.push_back(input);
 	emit_changed();
@@ -340,7 +340,7 @@ void AnimationNode::remove_input(int p_index) {
 
 bool AnimationNode::set_input_name(int p_input, const String &p_name) {
 	ERR_FAIL_INDEX_V(p_input, inputs.size(), false);
-	ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false);
+	ERR_FAIL_COND_V(p_name.contains_char('.') || p_name.contains_char('/'), false);
 	inputs.write[p_input].name = p_name;
 	emit_changed();
 	return true;

+ 2 - 2
scene/resources/animation_library.cpp

@@ -33,11 +33,11 @@
 #include "scene/scene_string_names.h"
 
 bool AnimationLibrary::is_valid_animation_name(const String &p_name) {
-	return !(p_name.is_empty() || p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("["));
+	return !(p_name.is_empty() || p_name.contains_char('/') || p_name.contains_char(':') || p_name.contains_char(',') || p_name.contains_char('['));
 }
 
 bool AnimationLibrary::is_valid_library_name(const String &p_name) {
-	return !(p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("["));
+	return !(p_name.contains_char('/') || p_name.contains_char(':') || p_name.contains_char(',') || p_name.contains_char('['));
 }
 
 String AnimationLibrary::validate_library_name(const String &p_name) {

+ 2 - 2
scene/resources/theme.cpp

@@ -37,7 +37,7 @@
 bool Theme::_set(const StringName &p_name, const Variant &p_value) {
 	String sname = p_name;
 
-	if (sname.contains("/")) {
+	if (sname.contains_char('/')) {
 		String type = sname.get_slicec('/', 1);
 		String theme_type = sname.get_slicec('/', 0);
 		String prop_name = sname.get_slicec('/', 2);
@@ -69,7 +69,7 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) {
 bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
 	String sname = p_name;
 
-	if (sname.contains("/")) {
+	if (sname.contains_char('/')) {
 		String type = sname.get_slicec('/', 1);
 		String theme_type = sname.get_slicec('/', 0);
 		String prop_name = sname.get_slicec('/', 2);

+ 1 - 1
scene/resources/visual_shader.cpp

@@ -4147,7 +4147,7 @@ String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::
 		if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
 			if (!p_input_vars[count].is_empty()) {
 				String s = ports[idx].string;
-				if (s.contains(":")) {
+				if (s.contains_char(':')) {
 					shader_code += "	" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
 				} else {
 					shader_code += "	" + s + " = " + p_input_vars[count] + ";\n";

+ 2 - 2
servers/rendering/rendering_device_binds.cpp

@@ -106,11 +106,11 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String
 		if (reading_versions) {
 			String l = line.strip_edges();
 			if (!l.is_empty()) {
-				if (!l.contains("=")) {
+				if (!l.contains_char('=')) {
 					base_error = "Missing `=` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";
 					break;
 				}
-				if (!l.contains(";")) {
+				if (!l.contains_char(';')) {
 					// We don't require a semicolon per se, but it's needed for clang-format to handle things properly.
 					base_error = "Missing `;` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";
 					break;

+ 1 - 1
servers/rendering/shader_compiler.cpp

@@ -181,7 +181,7 @@ static String _mkid(const String &p_id) {
 
 static String f2sp0(float p_float) {
 	String num = rtos(p_float);
-	if (!num.contains(".") && !num.contains("e")) {
+	if (!num.contains_char('.') && !num.contains_char('e')) {
 		num += ".0";
 	}
 	return num;