[messages] Move string extensions into analyzer_testing.

These extensions were previously in `pkg/analyzer_utilities`, which is
not published on `pub`. That meant they could not be used from within
the `lib` directory of any package that *is* published on
`pub`. Specifically, they could not be used from within
`pkg/analyzer_testing/lib`.

In a follow-up CL, I will modify the testing logic so that after
printing `To accept the current state, expect:`, it prints diagnostic
codes in their proper camelCase format.

Change-Id: I6a6a696432d7162906b2c235ea88310dc0aa1fa9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480040
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Paul Berry
2026-02-12 10:12:02 -08:00
committed by Commit Queue
parent c53054b9a8
commit 599f4bf94e
12 changed files with 109 additions and 98 deletions
@@ -12,7 +12,7 @@ import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart';
import 'package:analyzer_utilities/extensions/string.dart';
import 'package:analyzer_testing/utilities/extensions/string.dart';
import 'package:test/test.dart';
/// A diagnostic listener that collects all of the diagnostics passed to it for
+7
View File
@@ -65,6 +65,13 @@ package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart:
package:analyzer_testing/utilities/extensions/resource_provider.dart:
ResourceProviderExtension (extension on ResourceProvider):
convertPath (method: String Function(String))
package:analyzer_testing/utilities/extensions/string.dart:
StringExtension (extension on String):
isCamelCase (getter: bool)
isPascalCase (getter: bool)
toCamelCase (method: String Function())
toPascalCase (method: String Function())
toSnakeCase (method: String Function())
package:analyzer_testing/utilities/utilities.dart:
analysisOptionsContent (function: String Function({Map<String, Object?> errors, List<String> experiments, List<String> includes, List<String> legacyPlugins, bool propagateLinterExceptions, List<String> rules, bool strictCasts, bool strictInference, bool strictRawTypes, List<String> unignorableNames}))
pubspecYamlContent (function: String Function({List<String> dependencies, String? name, String? sdkVersion}))
@@ -0,0 +1,94 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
extension StringExtension on String {
static final _toSnakeCaseRegExp = RegExp('_?[A-Z]');
static final _startsWithLowerCaseRegExp = RegExp('^[a-z]');
static final _startsWithUpperCaseRegExp = RegExp('^[A-Z]');
/// Returns `true` if the string is a `camelCase` string.
bool get isCamelCase {
if (contains('_')) return false;
if (_startsWithLowerCaseRegExp.matchAsPrefix(this) == null) return false;
return true;
}
/// Returns `true` if the string is a `PascalCase` string.
bool get isPascalCase {
if (contains('_')) return false;
if (_startsWithUpperCaseRegExp.matchAsPrefix(this) == null) return false;
return true;
}
/// Converts `SCREAMING_SNAKE_CASE` or `snake_case` to `camelCase`.
String toCamelCase() {
var parts = toLowerCase().split('_');
var buffer = StringBuffer();
var i = 0;
// Preserve initial '_'s
while (i < parts.length - 1 && parts[i].isEmpty) {
buffer.write('_');
++i;
}
if (i < parts.length) {
// Convert first word to lower case
buffer.write(parts[i].toLowerCase());
++i;
// Convert remaining words to initial upper case
while (i < parts.length) {
var part = parts[i];
if (part.isNotEmpty) {
buffer.write(part[0].toUpperCase());
buffer.write(part.substring(1));
}
++i;
}
}
return buffer.toString();
}
/// Converts `SCREAMING_SNAKE_CASE` or `snake_case` to `PascalCase`.
String toPascalCase() {
var parts = toLowerCase().split('_');
var buffer = StringBuffer();
var i = 0;
// Preserve initial '_'s
while (i < parts.length - 1 && parts[i].isEmpty) {
buffer.write('_');
++i;
}
// Convert words to initial upper case
while (i < parts.length) {
var part = parts[i];
if (part.isNotEmpty) {
buffer.write(part[0].toUpperCase());
buffer.write(part.substring(1));
}
++i;
}
return buffer.toString();
}
/// Converts `camelCase` or `PascalCase` to `snake_case`
String toSnakeCase() {
var parts = <String>[];
var i = 0;
var wordStarts = _toSnakeCaseRegExp.allMatches(this);
for (var RegExpMatch(:start) in wordStarts) {
if (i < start) {
parts.add(substring(i, start).toLowerCase());
i = start;
}
if (this[i] == '_' && parts.isNotEmpty) {
// Avoid doubling up the `_`. This handles strings that are already in
// snake case like `foo_Bar` (which translates to `foo_bar`).
i++;
}
}
if (i < length) {
parts.add(substring(i).toLowerCase());
}
return parts.join('_');
}
}
+1
View File
@@ -24,3 +24,4 @@ dev_dependencies:
analyzer_utilities: any
args: any
lints: any
test_reflective_loader: any
@@ -6,7 +6,7 @@
// compatible with the `non_constant_identifier_names` lint.
// ignore_for_file: non_constant_identifier_names
import 'package:analyzer_utilities/extensions/string.dart';
import 'package:analyzer_testing/utilities/extensions/string.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -9,8 +9,8 @@ import 'dart:convert';
import 'dart:io';
import 'package:analyzer_testing/package_root.dart' as pkg_root;
import 'package:analyzer_testing/utilities/extensions/string.dart';
import 'package:analyzer_utilities/analyzer_message_constant_style.dart';
import 'package:analyzer_utilities/extensions/string.dart';
import 'package:analyzer_utilities/located_error.dart';
import 'package:analyzer_utilities/messages.dart';
import 'package:analyzer_utilities/tools.dart';
@@ -1,94 +1,3 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
extension StringExtension on String {
static final _toSnakeCaseRegExp = RegExp('_?[A-Z]');
static final _startsWithLowerCaseRegExp = RegExp('^[a-z]');
static final _startsWithUpperCaseRegExp = RegExp('^[A-Z]');
/// Returns `true` if the string is a `camelCase` string.
bool get isCamelCase {
if (contains('_')) return false;
if (_startsWithLowerCaseRegExp.matchAsPrefix(this) == null) return false;
return true;
}
/// Returns `true` if the string is a `PascalCase` string.
bool get isPascalCase {
if (contains('_')) return false;
if (_startsWithUpperCaseRegExp.matchAsPrefix(this) == null) return false;
return true;
}
/// Converts `SCREAMING_SNAKE_CASE` or `snake_case` to `camelCase`.
String toCamelCase() {
var parts = toLowerCase().split('_');
var buffer = StringBuffer();
var i = 0;
// Preserve initial '_'s
while (i < parts.length - 1 && parts[i].isEmpty) {
buffer.write('_');
++i;
}
if (i < parts.length) {
// Convert first word to lower case
buffer.write(parts[i].toLowerCase());
++i;
// Convert remaining words to initial upper case
while (i < parts.length) {
var part = parts[i];
if (part.isNotEmpty) {
buffer.write(part[0].toUpperCase());
buffer.write(part.substring(1));
}
++i;
}
}
return buffer.toString();
}
/// Converts `SCREAMING_SNAKE_CASE` or `snake_case` to `PascalCase`.
String toPascalCase() {
var parts = toLowerCase().split('_');
var buffer = StringBuffer();
var i = 0;
// Preserve initial '_'s
while (i < parts.length - 1 && parts[i].isEmpty) {
buffer.write('_');
++i;
}
// Convert words to initial upper case
while (i < parts.length) {
var part = parts[i];
if (part.isNotEmpty) {
buffer.write(part[0].toUpperCase());
buffer.write(part.substring(1));
}
++i;
}
return buffer.toString();
}
/// Converts `camelCase` or `PascalCase` to `snake_case`
String toSnakeCase() {
var parts = <String>[];
var i = 0;
var wordStarts = _toSnakeCaseRegExp.allMatches(this);
for (var RegExpMatch(:start) in wordStarts) {
if (i < start) {
parts.add(substring(i, start).toLowerCase());
i = start;
}
if (this[i] == '_' && parts.isNotEmpty) {
// Avoid doubling up the `_`. This handles strings that are already in
// snake case like `foo_Bar` (which translates to `foo_bar`).
i++;
}
}
if (i < length) {
parts.add(substring(i).toLowerCase());
}
return parts.join('_');
}
}
+1 -1
View File
@@ -9,8 +9,8 @@ import 'dart:convert';
import 'dart:io';
import 'package:analyzer_testing/package_root.dart' as pkg_root;
import 'package:analyzer_testing/utilities/extensions/string.dart';
import 'package:analyzer_utilities/analyzer_messages.dart';
import 'package:analyzer_utilities/extensions/string.dart';
import 'package:analyzer_utilities/lint_messages.dart';
import 'package:analyzer_utilities/located_error.dart';
import 'package:collection/collection.dart';
-1
View File
@@ -23,4 +23,3 @@ dependencies:
dev_dependencies:
analyzer_plugin: any
lints: any
test_reflective_loader: any
+1
View File
@@ -23,6 +23,7 @@ dependencies:
# Use 'any' constraints here; we get our versions from the DEPS file.
dev_dependencies:
analyzer: any
analyzer_testing: any
analyzer_utilities: any
args: any
build_integration: any
+1 -1
View File
@@ -13,8 +13,8 @@ import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
getMessageRelatedInformation;
import 'package:_fe_analyzer_shared/src/messages/severity.dart'
show CfeSeverity, severityEnumValues;
import 'package:analyzer_utilities/extensions/string.dart';
import 'package:analyzer_utilities/messages.dart';
import 'package:analyzer_testing/utilities/extensions/string.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show CompilerOptions, parseExperimentalArguments, parseExperimentalFlags;
import 'package:front_end/src/api_prototype/experimental_flags.dart'
@@ -5,7 +5,7 @@
/// @docImport 'package:front_end/src/codes/type_labeler.dart';
library;
import 'package:analyzer_utilities/extensions/string.dart';
import 'package:analyzer_testing/utilities/extensions/string.dart';
import 'package:analyzer_utilities/located_error.dart';
import 'package:analyzer_utilities/messages.dart';