From e385da67ea33a9af4623ea0c4a067587e776aef7 Mon Sep 17 00:00:00 2001 From: Robert Nystrom Date: Fri, 14 Nov 2025 15:09:53 -0800 Subject: [PATCH] Remove uses of internal analyzer implementation. In the very rare case that a user called formatStatement() and gave the formatter something longer than a single statement, we need to throw a FormatException but don't actually have a diagnostic from the analyzer to use. To deal with that, it used to import some internal implementation libraries in order to conjure up a diagnostic. Instead, this has a local implementation of the necessary public analyzer classes. This will avoid dart_style being broken with analyzer 9.0.0 when that internal code changes. --- CHANGELOG.md | 4 ++ lib/src/cli/formatter_options.dart | 2 +- lib/src/dart_formatter.dart | 74 ++++++++++++++++++++++++++---- pubspec.yaml | 2 +- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c51cdf6d..3dc28ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.4-wip + +* Remove dependencies on analyzer internal implementation. + ## 3.1.3 * No longer format imports with configurations and a prefix in the wrong order. diff --git a/lib/src/cli/formatter_options.dart b/lib/src/cli/formatter_options.dart index 4a5ffe46..1ca5d5a8 100644 --- a/lib/src/cli/formatter_options.dart +++ b/lib/src/cli/formatter_options.dart @@ -13,7 +13,7 @@ import 'show.dart'; import 'summary.dart'; // Note: The following line of code is modified by tool/grind.dart. -const dartStyleVersion = '3.1.3'; +const dartStyleVersion = '3.1.4'; /// Global options parsed from the command line that affect how the formatter /// produces and uses its outputs. diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart index dc28b372..85dbf4a5 100644 --- a/lib/src/dart_formatter.dart +++ b/lib/src/dart_formatter.dart @@ -9,10 +9,9 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/error/error.dart'; -// ignore: implementation_imports -import 'package:analyzer/src/dart/scanner/scanner.dart'; -// ignore: implementation_imports -import 'package:analyzer/src/string_source.dart'; +import 'package:analyzer/source/source.dart'; +import 'package:analyzer/source/timestamped_data.dart'; +import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; import 'exceptions.dart'; @@ -193,13 +192,16 @@ final class DartFormatter { // Make sure we consumed all of the source. var token = node.endToken.next!; if (token.type != TokenType.CLOSE_CURLY_BRACKET) { - var stringSource = StringSource(text, source.uri); - var error = Diagnostic.tmp( - source: stringSource, + var error = Diagnostic.forValues( + source: _StringSource(text, source.uri ?? ''), offset: token.offset - inputOffset, length: math.max(token.length, 1), - diagnosticCode: ParserErrorCode.unexpectedToken, - arguments: [token.lexeme], + diagnosticCode: _FormatterDiagnosticCode( + name: 'UnexpectedToken', + problemMessage: "Unexpected token '${token.lexeme}'.", + uniqueName: 'ParserErrorCode.UNEXPECTED_TOKEN', + ), + message: "Unexpected token '${token.lexeme}'.", ); throw FormatterException([error]); } @@ -273,3 +275,57 @@ enum TrailingCommas { /// trailing comma is preserved. preserve, } + +/// A custom [DiagnosticCode] implementation to avoid depending on internal +/// analyzer implementation. +class _FormatterDiagnosticCode extends DiagnosticCode { + _FormatterDiagnosticCode({ + required super.name, + required super.problemMessage, + required super.uniqueName, + }); + + @override + DiagnosticSeverity get severity => DiagnosticSeverity.ERROR; + + @override + DiagnosticType get type => DiagnosticType.SYNTACTIC_ERROR; +} + +/// An implementation of [Source] for an in-memory Dart string. +/// +/// Mostly copied from `package:analyzer/lib/src/string_source.dart`. Copied +/// here to avoid depending on internal implementation. +class _StringSource extends Source { + /// The content of the source. + final String _contents; + + @override + final String fullName; + + @override + Uri get uri => p.toUri(fullName); + + _StringSource(this._contents, this.fullName); + + @override + TimestampedData get contents => TimestampedData(0, _contents); + + @override + int get hashCode => _contents.hashCode ^ fullName.hashCode; + + @override + String get shortName => fullName; + + /// Return `true` if the given [object] is a string source that is equal to + /// this source. + @override + bool operator ==(Object object) { + return object is _StringSource && + object._contents == _contents && + object.fullName == fullName; + } + + @override + bool exists() => true; +} diff --git a/pubspec.yaml b/pubspec.yaml index 1180ab67..933209e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_style # Note: See tool/grind.dart for how to bump the version. -version: 3.1.3 +version: 3.1.4-wip description: >- Opinionated, automatic Dart source code formatter. Provides an API and a CLI tool.