1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 12:06:26 +00:00

Remove the obsolete dartfix package

Change-Id: Icc986d6345e2867ca0f3e3863dff6b0418db8242
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/169761
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Devon Carew <devoncarew@google.com>
This commit is contained in:
Brian Wilkerson 2020-10-30 17:22:31 +00:00 committed by commit-bot@chromium.org
parent 5efe55abf7
commit 224a997a23
36 changed files with 0 additions and 2392 deletions

View File

@ -228,12 +228,6 @@
"packageUri": "lib/",
"languageVersion": "2.10"
},
{
"name": "dartfix",
"rootUri": "../pkg/dartfix",
"packageUri": "lib/",
"languageVersion": "2.8"
},
{
"name": "dds",
"rootUri": "../pkg/dds",

View File

@ -35,7 +35,6 @@ dart_internal:pkg/dart_internal/lib
dart_style:third_party/pkg_tested/dart_style/lib
dartdev:pkg/dartdev/lib
dartdoc:third_party/pkg/dartdoc/lib
dartfix:pkg/dartfix/lib
dds:pkg/dds/lib
dev_compiler:pkg/dev_compiler/lib
diagnostic:pkg/diagnostic/lib

View File

@ -1,4 +0,0 @@
*.iml
.dart_tool
.packages
pubspec.lock

View File

@ -1,38 +0,0 @@
# 0.1.8
* The dartfix package has been deprecated. The functionality has been moved to
the new `dart fix` command that's included in the SDK.
# 0.1.7
* Improve experimental non-nullable migration support.
* Extract some nnbd migration implementation components from the dartfix
package.
# 0.1.6
* Improve experimental non-nullable migration support.
# 0.1.5
* Add command line options for selecting/excluding fixes to apply (`--fix`,
`--excludeFix`, and `--required`). Call with `--help` for more details.
* Add a `--color` option for printing messages with ANSI colors. This defaults
to true if the terminal supports ANSI colors.
* Add a `--pedantic` option for specifying fixes relating to the [pedantic]
lints.
* Add experimental non-nullable migration support.
[pedantic]: https://pub.dev/packages/pedantic
# 0.1.4
* update protocol version constraints
# 0.1.3
* update SDK constraints
# 0.1.2
* update SDK constraints
* add example.dart showing what can be "fixed"
# 0.1.1
* Remove reading dartfix version from pubspec
# 0.1.0
* Initial version

View File

@ -1,26 +0,0 @@
Copyright 2018, the Dart project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,82 +0,0 @@
`dartfix` is a command-line tool for migrating your Dart code
to use newer syntax styles.
## Usage
> **Important:**
> Save a copy of your source code before making changes with `dartfix`.
> Unlike [dartfmt][], which makes only safe changes (usually to whitespace),
> `dartfix` can make changes that you might need to undo or modify.
Before you can use the `dartfix` tool, you need to
[install it](#installing-and-updating-dartfix), as described below.
Then invoke it with the name of the directory that you want to update.
When you're ready to make the suggested changes,
add the `--overwrite` option.
```terminal
$ dartfix examples/misc
... summary of recommended changes ...
$ dartfix examples/misc --overwrite
```
## Features
`dartfix` applies different types of "fixes" to migrate your Dart code.
By default, all fixes are applied, but you can select only the specific fixes you
want. See `dartfix --help` for more about the available command line options.
Some of the fixes that you can apply are "required" in that the Dart language
is changing and at some point the old syntax will no longer be supported.
To only apply these changes, pass the `--required` option on the command line.
The required fixes include:
* Find classes used as mixins, and convert them to use the `mixin` keyword
instead of `class`.
Mixin support is one of the [features added to Dart in 2.1][].
At some point in the future, the Dart team plans
to disallow using classes as mixins.
* Move named constructor type arguments from the name to the type. <br>
For example, given `class A<T> { A.from(Object obj) { } }`,
`dartfix` changes constructor invocations in the following way:
```
Original code:
A.from<String>(anObject) // Invokes the `A.from` named constructor.
Code produced by dartfix:
A<String>.from(anObject) // Same, but the type is directly after `A`.
```
Other changes are recommended but not required. These include:
* Find `double` literals that end in `.0`, and remove the `.0`.
Language support for this was [added in Dart in 2.1][].
## Installing and updating dartfix
The easiest way to use `dartfix` is to [globally install][] it,
so that it can be [in your path][PATH]:
```terminal
$ pub global activate dartfix
```
Use the same command to update `dartfix`.
We recommend updating `dartfix` whenever you update your Dart SDK
or when a new feature is released.
## Filing issues
If you want a new fix, first look at [dartfix issues][]
and star the fixes you want.
If no issue exists for the fix, [create a GitHub issue.][new issue]
[dartfix]: https://pub.dev/packages/dartfix
[dartfmt]: https://dart.dev/tools/dartfmt
[added in Dart in 2.1]: https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#210---2018-11-15
[features added to Dart in 2.1]: https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#210---2018-11-15
[globally install]: https://dart.dev/tools/pub/cmd/pub-global
[new issue]: https://github.com/dart-lang/sdk/issues/new?title=dartfix%20request%3A%20%3CSUMMARIZE%20REQUEST%20HERE%3E
[dartfix issues]: https://github.com/dart-lang/sdk/issues?q=is%3Aissue+is%3Aopen+label%3Aanalyzer-dartfix
[PATH]: https://dart.dev/tools/pub/cmd/pub-global#running-a-script-from-your-path

View File

@ -1,42 +0,0 @@
include: package:pedantic/analysis_options.1.8.0.yaml
analyzer:
strong-mode:
implicit-casts: false
errors:
# Increase the severity of the unused_import hint.
unused_import: warning
unnecessary_brace_in_string_interps: warning
# There are many pre-existing violations; this lint may not work well with
# the Analyzer team's style.
omit_local_variable_types: ignore
# Turn off the prefer_is_empty lint so that it can be used in the example tests
# and not be reported during normal package analysis.
prefer_is_empty: ignore
linter:
rules:
- await_only_futures
- directives_ordering
- empty_statements
- unnecessary_brace_in_string_interps
#
# Delta from pedantic 1.8.0 to 1.9.0
#
#- always_declare_return_types # 17
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_null_checks_in_equality_operators
- camel_case_extensions
#- omit_local_variable_types # 44
- prefer_adjacent_string_concatenation
- prefer_collection_literals
- prefer_conditional_assignment
#- prefer_final_fields # 1
- prefer_for_elements_to_map_fromIterable
- prefer_generic_function_type_aliases
#- prefer_if_null_operators # 2
- prefer_single_quotes
- prefer_spread_collections
- unnecessary_this
- use_function_type_syntax_for_parameters

View File

@ -1,14 +0,0 @@
#!/usr/bin/env dart
// Copyright (c) 2018, 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.
import 'package:dartfix/src/driver.dart';
/// The entry point for dartfix.
void main(List<String> args) async {
Driver starter = Driver();
// Wait for the starter to complete.
await starter.start(args);
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2019, 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.
// This file contains code that is modified by running dartfix.
// After running dartfix, this content matches a file in the "fixed" directory.
// Dart will automatically convert int literals to doubles.
// Running dartfix converts this double literal to an int
// if --double-to-int is specified on the command line.
const double myDouble = 4.0;
// This class is used as a mixin but does not use the new mixin syntax.
// Running dartfix converts this class to use the new syntax.
class MyMixin {
final someValue = myDouble;
}
class MyClass with MyMixin {}
void main(List<String> args) {
if (args.length == 0) {
print('myDouble = ${MyClass().someValue}');
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2019, 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.
// This file contains code that has been modified by running dartfix.
// See example.dart for the original unmodified code.
// Dart will automatically convert int literals to doubles.
// Running dartfix converts this double literal to an int
// if --double-to-int is specified on the command line.
const double myDouble = 4;
// This class is used as a mixin but does not use the new mixin syntax.
// Running dartfix converts this class to use the new syntax.
mixin MyMixin {
final someValue = myDouble;
}
class MyClass with MyMixin {}
void main(List<String> args) {
if (args.length == 0) {
print('myDouble = ${MyClass().someValue}');
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2019, 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.
// This file contains code that has been modified by running dartfix.
// See example.dart for the original unmodified code.
// Dart will automatically convert int literals to doubles.
// Running dartfix converts this double literal to an int
// if --double-to-int is specified on the command line.
const double myDouble = 4.0;
// This class is used as a mixin but does not use the new mixin syntax.
// Running dartfix converts this class to use the new syntax.
class MyMixin {
final someValue = myDouble;
}
class MyClass with MyMixin {}
void main(List<String> args) {
if (args.isEmpty) {
print('myDouble = ${MyClass().someValue}');
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2019, 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.
// This file contains code that has been modified by running dartfix.
// See example.dart for the original unmodified code.
// Dart will automatically convert int literals to doubles.
// Running dartfix converts this double literal to an int
// if --double-to-int is specified on the command line.
const double myDouble = 4.0;
// This class is used as a mixin but does not use the new mixin syntax.
// Running dartfix converts this class to use the new syntax.
class MyMixin {
final someValue = myDouble;
}
class MyClass with MyMixin {}
void main(List<String> args) {
if (args.isEmpty) {
print('myDouble = ${MyClass().someValue}');
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2019, 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.
// This file contains code that has been modified by running dartfix.
// See example.dart for the original unmodified code.
// Dart will automatically convert int literals to doubles.
// Running dartfix converts this double literal to an int
// if --double-to-int is specified on the command line.
const double myDouble = 4.0;
// This class is used as a mixin but does not use the new mixin syntax.
// Running dartfix converts this class to use the new syntax.
mixin MyMixin {
final someValue = myDouble;
}
class MyClass with MyMixin {}
void main(List<String> args) {
if (args.length == 0) {
print('myDouble = ${MyClass().someValue}');
}
}

View File

@ -1,31 +0,0 @@
// Copyright (c) 2018, 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.
//
// This file has been automatically generated. Please do not edit it manually.
// To regenerate the file, use the script
// "pkg/analysis_server/tool/spec/generate_files".
import 'dart:async';
import 'package:analysis_server_client/handler/notification_handler.dart';
import 'package:analysis_server_client/protocol.dart';
/// [AnalysisCompleteHandler] listens to analysis server notifications
/// and detects when analysis has finished.
mixin AnalysisCompleteHandler on NotificationHandler {
Completer<void> _analysisComplete;
@override
void onServerStatus(ServerStatusParams params) {
if (params.analysis != null && !params.analysis.isAnalyzing) {
_analysisComplete?.complete();
_analysisComplete = null;
}
}
Future<void> analysisComplete() {
_analysisComplete ??= Completer<void>();
return _analysisComplete.future;
}
}

View File

@ -1,53 +0,0 @@
// Copyright (c) 2018, 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.
import 'package:analysis_server_client/listener/server_listener.dart';
/// [BadMessageListener] throws an exception if the [Client] receives bad data.
mixin BadMessageListener on ServerListener {
/// True if we've received bad data from the server.
bool _receivedBadDataFromServer = false;
void throwDelayedException(String prefix, String details) {
if (!_receivedBadDataFromServer) {
_receivedBadDataFromServer = true;
// Give the server 1 second to continue outputting bad data
// such as outputting a stacktrace.
Future.delayed(Duration(seconds: 1), () {
throw '$prefix $details';
});
}
}
@override
void badMessage(String trimmedLine, exception) {
super.badMessage(trimmedLine, exception);
throwDelayedException('JSON decode failure', '$exception');
}
@override
void errorMessage(String line) {
super.errorMessage(line);
throwDelayedException('ERR:', line);
}
@override
void unexpectedMessage(Map<String, dynamic> message) {
super.unexpectedMessage(message);
throwDelayedException(
'BAD DATA FROM SERVER:', 'Unexpected message from server');
}
@override
void unexpectedResponse(Map<String, dynamic> message, id) {
super.unexpectedResponse(message, id);
throw 'Unexpected response from server: id=$id';
}
@override
void unexpectedStop(int exitCode) {
super.unexpectedStop(exitCode);
throwDelayedException('Server terminated with exit code', '$exitCode');
}
}

View File

@ -1,58 +0,0 @@
// Copyright (c) 2018, 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.
import 'package:analysis_server_client/listener/server_listener.dart';
import 'package:analysis_server_client/server.dart';
import 'package:dartfix/listener/bad_message_listener.dart';
import 'package:dartfix/listener/timed_listener.dart';
/// [RecordingListener] caches all messages exchanged with the server
/// and print them if a problem occurs.
///
/// This is primarily used when testing and debugging the analysis server.
class RecordingListener with ServerListener, BadMessageListener, TimedListener {
/// True if we are currently printing out messages exchanged with the server.
bool _echoMessages = false;
/// Messages which have been exchanged with the server; we buffer these
/// up until the test finishes, so that they can be examined in the debugger
/// or printed out in response to a call to [echoMessages].
final _messages = <String>[];
/// Print out any messages exchanged with the server. If some messages have
/// already been exchanged with the server, they are printed out immediately.
void echoMessages() {
if (_echoMessages) {
return;
}
_echoMessages = true;
for (String line in _messages) {
print(line);
}
}
/// Called when the [Server] is terminating the server process
/// rather than requesting that the server stop itself.
@override
void killingServerProcess(String reason) {
echoMessages();
super.killingServerProcess(reason);
}
/// Log a timed message about interaction with the server.
@override
void logTimed(double elapseTime, String prefix, String details) {
String line = '$elapseTime: $prefix $details';
if (_echoMessages) {
print(line);
}
_messages.add(line);
}
@override
void throwDelayedException(String prefix, String details) {
echoMessages();
super.throwDelayedException(prefix, details);
}
}

View File

@ -1,39 +0,0 @@
// Copyright (c) 2018, 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.
import 'package:analysis_server_client/listener/server_listener.dart';
/// [TimedListener] appends a timestamp (seconds since server startup)
/// to each logged interaction with the server.
mixin TimedListener on ServerListener {
/// Stopwatch that we use to generate timing information for debug output.
final Stopwatch _time = Stopwatch();
/// The [currentElapseTime] at which the last communication was received from
/// the server or `null` if no communication has been received.
double lastCommunicationTime;
/// The current elapse time (seconds) since the server was started.
double get currentElapseTime => _time.elapsedTicks / _time.frequency;
@override
void log(String prefix, String details) {
logTimed(currentElapseTime, prefix, details);
}
/// Log a timed message about interaction with the server.
void logTimed(double elapseTime, String prefix, String details);
@override
void messageReceived(String json) {
lastCommunicationTime = currentElapseTime;
super.messageReceived(json);
}
@override
void startingServer(String dartBinary, List<String> arguments) {
_time.start();
super.startingServer(dartBinary, arguments);
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2018, 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.
import 'dart:io' as io;
/// The context for dartfix.
class Context {
String get workingDir => io.Directory.current.path;
bool exists(String filePath) {
return io.FileSystemEntity.typeSync(filePath) !=
io.FileSystemEntityType.notFound;
}
void exit(int code) {
io.exit(code);
}
bool isDirectory(String filePath) {
return io.FileSystemEntity.typeSync(filePath) ==
io.FileSystemEntityType.directory;
}
}

View File

@ -1,508 +0,0 @@
// Copyright (c) 2018, 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.
import 'dart:io' show File, Platform;
import 'package:analysis_server_client/handler/connection_handler.dart';
import 'package:analysis_server_client/handler/notification_handler.dart';
import 'package:analysis_server_client/listener/server_listener.dart';
import 'package:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/server.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dartfix/handler/analysis_complete_handler.dart';
import 'package:dartfix/listener/bad_message_listener.dart';
import 'package:dartfix/src/context.dart';
import 'package:dartfix/src/options.dart';
import 'package:dartfix/src/util.dart';
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
import 'migrate/display.dart';
import 'util.dart';
class Driver {
Context context;
_Handler handler;
Logger logger;
Server server;
bool force;
bool overwrite;
List<String> targets;
EditDartfixResult result;
Ansi get ansi => logger.ansi;
/// Apply the fixes that were computed.
void applyFixes() {
for (SourceFileEdit fileEdit in result.edits) {
final file = File(fileEdit.file);
String code = file.existsSync() ? file.readAsStringSync() : '';
code = SourceEdit.applySequence(code, fileEdit.edits);
file.writeAsStringSync(code);
}
}
bool checkIfChangesShouldBeApplied(EditDartfixResult result) {
logger.stdout('');
if (result.hasErrors) {
logger.stdout('WARNING: The analyzed source contains errors'
' that might affect the accuracy of these changes.');
logger.stdout('');
if (!force) {
logger.stdout('Rerun with --$forceOption to apply these changes.');
return false;
}
} else if (!overwrite && !force) {
logger.stdout('Rerun with --$overwriteOption to apply these changes.');
return false;
}
return true;
}
/// Check if the specified options is supported by the version of analysis
/// server being run and return `true` if they are.
/// Display an error message and return `false` if not.
bool checkIfSelectedOptionsAreSupported(Options options) {
if (handler.serverProtocolVersion.compareTo(Version(1, 27, 2)) >= 0) {
return true;
}
if (options.pedanticFixes) {
_unsupportedOption(pedanticOption);
return false;
}
if (handler.serverProtocolVersion.compareTo(Version(1, 22, 2)) >= 0) {
return true;
}
if (options.excludeFixes.isNotEmpty) {
_unsupportedOption(excludeFixOption);
return false;
}
if (options.includeFixes.isNotEmpty) {
_unsupportedOption(includeFixOption);
return false;
}
if (options.showHelp) {
return false;
}
return true;
}
void printAndApplyFixes() {
showDescriptions('Recommended changes that cannot be automatically applied',
result.otherSuggestions);
showDetails(result.details);
if (result.edits.isEmpty) {
logger.stdout('');
logger.stdout(result.otherSuggestions.isNotEmpty
? 'None of the recommended changes can be automatically applied.'
: 'No recommended changes.');
return;
}
logger.stdout('');
logger.stdout(ansi.emphasized('Files to be changed:'));
for (SourceFileEdit fileEdit in result.edits) {
logger.stdout(' ${_relativePath(fileEdit.file)}');
}
if (checkIfChangesShouldBeApplied(result)) {
applyFixes();
logger.stdout(ansi.emphasized('Changes have been applied.'));
}
}
Future<EditDartfixResult> requestFixes(
Options options, {
Progress progress,
}) async {
Future isAnalysisComplete = handler.analysisComplete();
final params = EditDartfixParams(options.targets);
if (options.excludeFixes.isNotEmpty) {
params.excludedFixes = options.excludeFixes;
}
if (options.includeFixes.isNotEmpty) {
params.includedFixes = options.includeFixes;
}
if (options.pedanticFixes) {
params.includePedanticFixes = true;
}
Map<String, dynamic> json =
await server.send(EDIT_REQUEST_DARTFIX, params.toJson());
// TODO(danrubel): This is imprecise signal for determining when all
// analysis error notifications have been received. Consider adding a new
// notification indicating that the server is idle (all requests processed,
// all analysis complete, all notifications sent).
await isAnalysisComplete;
progress.finish(showTiming: true);
ResponseDecoder decoder = ResponseDecoder(null);
return EditDartfixResult.fromJson(decoder, 'result', json);
}
/// Return `true` if the changes should be applied.
bool shouldApplyFixes(EditDartfixResult result) {
return overwrite || force;
}
void showDescriptions(String title, List<DartFixSuggestion> suggestions) {
if (suggestions.isNotEmpty) {
logger.stdout('');
logger.stdout(ansi.emphasized('$title:'));
List<DartFixSuggestion> sorted = List.from(suggestions)
..sort(compareSuggestions);
for (DartFixSuggestion suggestion in sorted) {
final msg = StringBuffer();
msg.write(' ${toSentenceFragment(suggestion.description)}');
final loc = suggestion.location;
if (loc != null) {
msg.write('${_relativePath(loc.file)}');
msg.write('${loc.startLine}:${loc.startColumn}');
}
logger.stdout(msg.toString());
}
}
}
void showDetails(List<String> details) {
if (details == null || details.isEmpty) {
return;
}
logger.stdout('''
Analysis Details:
''');
for (String detail in details) {
logger.stdout('''
$detail
''');
}
}
void showFix(DartFix fix) {
logger.stdout('''
${ansi.emphasized(fix.name)}''');
if (fix.description != null) {
for (String line in _indentAndWrapDescription(fix.description)) {
logger.stdout(line);
}
}
}
Future<EditGetDartfixInfoResult> showFixes({Progress progress}) async {
Map<String, dynamic> json = await server.send(
EDIT_REQUEST_GET_DARTFIX_INFO, EditGetDartfixInfoParams().toJson());
progress?.finish(showTiming: true);
ResponseDecoder decoder = ResponseDecoder(null);
final result = EditGetDartfixInfoResult.fromJson(decoder, 'result', json);
final fixes = List<DartFix>.from(result.fixes)
..sort((f1, f2) => f1.name.compareTo(f2.name));
logger.stdout('''
These fixes can be enabled using --$includeFixOption:''');
fixes
..sort(compareFixes)
..forEach(showFix);
return result;
}
Future start(
List<String> args, {
Context testContext,
Logger testLogger,
}) async {
final Options options = Options.parse(args, testContext, testLogger);
force = options.force;
overwrite = options.overwrite;
targets = options.targets;
context = testContext ?? options.context;
logger = testLogger ?? options.logger;
server = Server(listener: _Listener(logger));
handler = _Handler(this, context);
// Start showing progress before we start the analysis server.
Progress progress;
if (options.showHelp) {
progress = logger.progress('${ansi.emphasized('Listing fixes')}');
} else {
progress = logger.progress('${ansi.emphasized('Calculating fixes')}');
}
if (!await startServer(options)) {
context.exit(16);
}
if (!checkIfSelectedOptionsAreSupported(options)) {
await server.stop();
context.exit(1);
}
if (options.showHelp) {
try {
await showFixes(progress: progress);
} finally {
await server.stop();
}
context.exit(0);
}
if (options.includeFixes.isEmpty && !options.pedanticFixes) {
logger.stdout('No fixes specified.');
context.exit(1);
}
Future serverStopped;
try {
await startServerAnalysis(options);
result = await requestFixes(options, progress: progress);
var fileEdits = result.edits;
var editCount = 0;
for (SourceFileEdit fileEdit in fileEdits) {
editCount += fileEdit.edits.length;
}
logger.stdout('Found $editCount changes in ${fileEdits.length} files.');
previewFixes(logger, result);
//
// Stop the server.
//
serverStopped = server.stop();
logger.stdout('');
// Check if we should apply fixes.
if (result.edits.isEmpty) {
logger.stdout(result.otherSuggestions.isNotEmpty
? 'None of the recommended changes can be automatically applied.'
: 'There are no recommended changes.');
} else if (shouldApplyFixes(result)) {
applyFixes();
logger.stdout('Changes have been applied.');
} else {
logger.stdout('Re-run with --overwrite to apply the above changes.');
}
await serverStopped;
} finally {
// If we didn't already try to stop the server, then stop it now.
if (serverStopped == null) {
await server.stop();
}
}
}
Future<bool> startServer(Options options) async {
// Automatically run analysis server from source
// if this command line tool is being run from source within the SDK repo.
String serverPath = options.serverSnapshot ?? findServerPath();
if (options.verbose) {
logger.trace('''
Dart SDK version ${Platform.version}
${Platform.resolvedExecutable}
dartfix
${Platform.script.toFilePath()}
analysis server
$serverPath
''');
}
await server.start(
clientId: 'dartfix',
clientVersion: 'unspecified',
sdkPath: options.sdkPath,
serverPath: serverPath,
);
server.listenToOutput(notificationProcessor: handler.handleEvent);
return handler.serverConnected(timeLimit: const Duration(seconds: 30));
}
Future<Progress> startServerAnalysis(
Options options, {
Progress progress,
}) async {
logger.trace('');
logger.trace('Setup analysis');
await server.send(SERVER_REQUEST_SET_SUBSCRIPTIONS,
ServerSetSubscriptionsParams([ServerService.STATUS]).toJson());
await server.send(
ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS,
AnalysisSetAnalysisRootsParams(
options.targets,
const [],
).toJson());
return progress;
}
List<String> _indentAndWrapDescription(String description) =>
description.split('\n').map((line) => ' $line').toList();
String _relativePath(String filePath) {
for (String target in targets) {
if (filePath.startsWith(target)) {
return filePath.substring(target.length + 1);
}
}
return filePath;
}
void _unsupportedOption(String option) {
final version = handler.serverProtocolVersion.toString();
logger.stderr('''
The --$option option is not supported by analysis server version $version.
Please upgrade to a newer version of the Dart SDK to use this option.''');
}
void previewFixes(
Logger logger,
EditDartfixResult results,
) {
final Ansi ansi = logger.ansi;
Map<String, List<DartFixSuggestion>> fileSuggestions = {};
for (DartFixSuggestion suggestion in results.suggestions) {
String file = suggestion.location.file;
fileSuggestions.putIfAbsent(file, () => <DartFixSuggestion>[]);
fileSuggestions[file].add(suggestion);
}
// present a diff-like view
for (SourceFileEdit sourceFileEdit in results.edits) {
String file = sourceFileEdit.file;
String relPath = path.relative(file);
int count = sourceFileEdit.edits.length;
logger.stdout('');
logger.stdout('${ansi.emphasized(relPath)} '
'($count ${pluralize('change', count)}):');
String source;
try {
source = File(file).readAsStringSync();
} catch (_) {}
if (source == null) {
logger.stdout(' (unable to retrieve source for file)');
} else {
SourcePrinter sourcePrinter = SourcePrinter(source);
List<SourceEdit> edits = sortEdits(sourceFileEdit);
// Apply edits.
sourcePrinter.applyEdits(edits);
// Render the changed lines.
sourcePrinter.processChangedLines((lineNumber, lineText) {
String prefix = ' line ${lineNumber.toString().padRight(3)}';
logger.stdout('$prefix ${lineText.trim()}');
});
}
}
}
}
class _Handler
with NotificationHandler, ConnectionHandler, AnalysisCompleteHandler {
final Driver driver;
final Logger logger;
final Context context;
@override
final Server server;
Version serverProtocolVersion;
_Handler(this.driver, this.context)
: logger = driver.logger,
server = driver.server;
@override
bool checkServerProtocolVersion(Version version) {
serverProtocolVersion = version;
return super.checkServerProtocolVersion(version);
}
@override
void onAnalysisErrors(AnalysisErrorsParams params) {
List<AnalysisError> errors = params.errors;
bool foundAtLeastOneError = false;
for (AnalysisError error in errors) {
if (shouldShowError(error)) {
if (!foundAtLeastOneError) {
foundAtLeastOneError = true;
logger.stdout('${driver._relativePath(params.file)}:');
}
Location loc = error.location;
logger.stdout(' ${toSentenceFragment(error.message)}'
'${loc.startLine}:${loc.startColumn}');
}
}
super.onAnalysisErrors(params);
// Analysis errors are non-fatal; no need to exit.
}
@override
void onFailedToConnect() {
logger.stderr('Failed to connect to server');
super.onFailedToConnect();
// Exiting on connection failure is already handled by [Driver.start].
}
@override
void onProtocolNotSupported(Version version) {
logger.stderr('Expected protocol version $PROTOCOL_VERSION,'
' but found $version');
final expectedVersion = Version.parse(PROTOCOL_VERSION);
if (version > expectedVersion) {
logger.stdout('''
This version of dartfix is incompatible with the current Dart SDK.
Try installing a newer version of dartfix by running:
pub global activate dartfix
''');
} else {
logger.stdout('''
This version of dartfix is too new to be used with the current Dart SDK. Try
upgrading the Dart SDK to a newer version or installing an older version of
dartfix using:
pub global activate dartfix <version>
''');
}
super.onProtocolNotSupported(version);
// This is handled by the connection failure case; no need to exit here.
}
@override
void onServerError(ServerErrorParams params) {
if (params.isFatal) {
logger.stderr('Fatal Server Error: ${params.message}');
} else {
logger.stderr('Server Error: ${params.message}');
}
if (params.stackTrace != null) {
logger.stderr(params.stackTrace);
}
super.onServerError(params);
// Server is stopped by super method, so we can safely exit here.
context.exit(16);
}
}
class _Listener with ServerListener, BadMessageListener {
final Logger logger;
final bool verbose;
_Listener(this.logger) : verbose = logger.isVerbose;
@override
void log(String prefix, String details) {
if (verbose) {
logger.trace('$prefix $details');
}
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2020, 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.
import 'package:analysis_server_client/protocol.dart';
import '../util.dart';
// TODO(devoncarew): This is only referenced from tests.
/// Perform the indicated source edits to the given source, returning the
/// resulting transformed text.
String applyEdits(SourceFileEdit sourceFileEdit, String source) {
List<SourceEdit> edits = sortEdits(sourceFileEdit);
return SourceEdit.applySequence(source, edits);
}

View File

@ -1,88 +0,0 @@
// Copyright (c) 2020, 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.
import 'package:analysis_server_client/protocol.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:path/path.dart' as path;
// TODO(devoncarew): This is only referenced from tests.
/// Given a Logger and an analysis issue, render the issue to the logger.
class IssueRenderer {
final Logger logger;
final String rootDirectory;
IssueRenderer(this.logger, this.rootDirectory);
void render(AnalysisError issue) {
// severity Message ... at foo/bar.dart:6:1 (error_code)
final Ansi ansi = logger.ansi;
logger.stdout(
' ${ansi.error(issue.severity.name.toLowerCase())}'
'${ansi.emphasized(_removePeriod(issue.message))} '
'at ${path.relative(issue.location.file, from: rootDirectory)}'
':${issue.location.startLine}:'
'${issue.location.startColumn} '
'• (${issue.code})',
);
}
}
typedef LineProcessor = void Function(int lineNumber, String lineText);
class SourcePrinter {
static final String red = '\u001b[31m';
static final String bold = '\u001b[1m';
static final String reversed = '\u001b[7m';
static final String none = '\u001b[0m';
String source;
SourcePrinter(this.source);
void applyEdits(List<SourceEdit> edits) {
for (SourceEdit edit in edits) {
if (edit.replacement.isNotEmpty) {
// an addition
insertText(edit.offset + edit.length, edit.replacement);
}
if (edit.length != 0) {
// a removal
deleteRange(edit.offset, edit.length);
}
}
}
void deleteRange(int offset, int length) {
source = source.substring(0, offset) +
red +
reversed +
source.substring(offset, offset + length) +
none +
source.substring(offset + length);
}
void insertText(int offset, String text) {
text = '$reversed$text$none';
source = source.substring(0, offset) + text + source.substring(offset);
}
void processChangedLines(LineProcessor callback) {
List<String> lines = source.split('\n');
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line.contains(none)) {
callback(i + 1, line);
}
}
}
}
String _removePeriod(String value) {
return value.endsWith('.') ? value.substring(0, value.length - 1) : value;
}

View File

@ -1,96 +0,0 @@
// Copyright (c) 2020, 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.
import 'dart:io';
import 'package:args/args.dart';
import 'package:args/src/arg_parser.dart';
import 'package:path/path.dart' as path;
// TODO(devoncarew): This class is unused.
class MigrateOptions {
static const applyChangesOption = 'apply-changes';
static const debugOption = 'debug';
static const ignoreErrorsOption = 'ignore-errors';
static const previewPortOption = 'preview-port';
static const sdkPathOption = 'sdk-path';
static const serverPathOption = 'server-path';
static const webPreviewOption = 'web-preview';
final bool applyChanges;
final bool debug;
final String directory;
final bool ignoreErrors;
final int previewPort;
final String serverPath;
final String sdkPath;
final bool webPreview;
MigrateOptions(ArgResults argResults, this.directory)
: applyChanges = argResults[applyChangesOption] as bool,
debug = argResults[debugOption] as bool,
ignoreErrors = argResults[ignoreErrorsOption] as bool,
previewPort =
int.tryParse(argResults[previewPortOption] as String) ?? 0,
sdkPath = argResults[sdkPathOption] as String,
serverPath = argResults[serverPathOption] as String,
webPreview = argResults['web-preview'] as bool;
String get directoryAbsolute => Directory(path.canonicalize(directory)).path;
@override
String toString() {
return '[$directory]';
}
static void defineOptions(ArgParser argParser) {
argParser.addFlag(
applyChangesOption,
defaultsTo: false,
negatable: false,
help: 'Apply the proposed null safety changes to the files on disk.',
);
argParser.addFlag(
debugOption,
defaultsTo: false,
hide: true,
negatable: true,
help: 'Show (very verbose) debugging information to stdout during '
'migration',
);
argParser.addFlag(
ignoreErrorsOption,
defaultsTo: false,
negatable: false,
help: 'Attempt to perform null safety analysis even if the package has '
'analysis errors.',
);
argParser.addOption(
sdkPathOption,
hide: true,
help: 'Override the SDK path used for migration.',
);
argParser.addOption(
previewPortOption,
defaultsTo: '0',
help: 'Run the preview server on the specified port. If not specified '
'or invalid, dynamically allocate a port.',
);
argParser.addOption(
serverPathOption,
hide: true,
help: 'Override the analysis server path used for migration.',
);
argParser.addFlag(
webPreviewOption,
defaultsTo: true,
negatable: true,
help: 'Show an interactive preview of the proposed null safety changes '
'in a browser window.\n'
'With --no-web-preview, the proposed changes are instead printed to '
'the console.',
);
}
}

View File

@ -1,210 +0,0 @@
// Copyright (c) 2018, 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.
import 'dart:io';
import 'package:args/args.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dartfix/src/context.dart';
import 'package:path/path.dart' as path;
// TODO(brianwilkerson) Deprecate 'excludeFix' and replace it with 'exclude-fix'
const excludeFixOption = 'excludeFix';
const forceOption = 'force';
const includeFixOption = 'fix';
const overwriteOption = 'overwrite';
const pedanticOption = 'pedantic';
const previewDirOption = 'preview-dir';
const previewPortOption = 'preview-port';
const sdkOption = 'sdk';
const _binaryName = 'dartfix';
const _colorOption = 'color';
// options only supported by server 1.22.2 and greater
const _helpOption = 'help';
const _serverSnapshot = 'server';
// options not supported yet by any server
const _verboseOption = 'verbose';
/// Command line options for `dartfix`.
class Options {
final Context context;
Logger logger;
List<String> targets;
final String sdkPath;
final String serverSnapshot;
final bool pedanticFixes;
final List<String> includeFixes;
final List<String> excludeFixes;
final bool force;
final bool showHelp;
bool overwrite;
final bool useColor;
final bool verbose;
Options._fromArgs(this.context, ArgResults results)
: force = results[forceOption] as bool,
includeFixes = (results[includeFixOption] as List ?? []).cast<String>(),
excludeFixes = (results[excludeFixOption] as List ?? []).cast<String>(),
overwrite = results[overwriteOption] as bool,
pedanticFixes = results[pedanticOption] as bool,
sdkPath = results[sdkOption] as String ?? _getSdkPath(),
serverSnapshot = results[_serverSnapshot] as String,
showHelp = results[_helpOption] as bool || results.arguments.isEmpty,
targets = results.rest,
useColor = results.wasParsed(_colorOption)
? results[_colorOption] as bool
: null,
verbose = results[_verboseOption] as bool;
String makeAbsoluteAndNormalize(String target) {
if (!path.isAbsolute(target)) {
target = path.join(context.workingDir, target);
}
return path.normalize(target);
}
static Options parse(List<String> args, Context context, Logger logger) {
final parser = ArgParser(allowTrailingOptions: true)
..addSeparator('Choosing fixes to be applied:')
..addMultiOption(includeFixOption,
help: 'Include a specific fix.', valueHelp: 'name-of-fix')
..addMultiOption(excludeFixOption,
help: 'Exclude a specific fix.', valueHelp: 'name-of-fix')
..addFlag(pedanticOption,
help: 'Apply pedantic fixes.', defaultsTo: false, negatable: false)
..addSeparator('Modifying files:')
..addFlag(overwriteOption,
abbr: 'w',
help: 'Overwrite files with the changes.',
defaultsTo: false,
negatable: false)
..addFlag(forceOption,
abbr: 'f',
help: 'Overwrite files even if there are errors.',
defaultsTo: false,
negatable: false)
..addSeparator('Miscellaneous:')
..addFlag(_helpOption,
abbr: 'h',
help: 'Display this help message.',
defaultsTo: false,
negatable: false)
..addOption(sdkOption,
help: 'Path to the SDK to analyze against.',
valueHelp: 'path',
hide: true)
..addOption(_serverSnapshot,
help: 'Path to the analysis server snapshot file.',
valueHelp: 'path',
hide: true)
..addFlag(_verboseOption,
abbr: 'v',
help: 'Verbose output.',
defaultsTo: false,
negatable: false)
..addFlag(_colorOption,
help: 'Use ansi colors when printing messages.',
defaultsTo: Ansi.terminalSupportsAnsi);
context ??= Context();
ArgResults results;
try {
results = parser.parse(args);
} on FormatException catch (e) {
logger ??= Logger.standard(ansi: Ansi(Ansi.terminalSupportsAnsi));
logger.stderr(e.message);
_showUsage(parser, logger);
context.exit(17);
}
Options options = Options._fromArgs(context, results);
if (logger == null) {
if (options.verbose) {
logger = Logger.verbose();
} else {
logger = Logger.standard(
ansi: Ansi(
options.useColor ?? Ansi.terminalSupportsAnsi,
));
}
}
options.logger = logger;
// For '--help', we short circuit the logic to validate the sdk and project.
if (options.showHelp) {
_showUsage(parser, logger, showHelpHint: false);
return options;
}
// Validate the Dart SDK location
String sdkPath = options.sdkPath;
if (sdkPath == null) {
logger.stderr('No Dart SDK found.');
context.exit(18);
}
if (!context.exists(sdkPath)) {
logger.stderr('Invalid Dart SDK path: $sdkPath');
context.exit(19);
}
// Check for files and/or directories to analyze.
if (options.targets == null || options.targets.isEmpty) {
logger.stderr('Expected at least one file or directory to analyze.');
context.exit(20);
}
// Normalize and verify paths
options.targets =
options.targets.map<String>(options.makeAbsoluteAndNormalize).toList();
for (String target in options.targets) {
if (!context.isDirectory(target)) {
if (!context.exists(target)) {
logger.stderr('Target does not exist: $target');
} else {
logger.stderr('Expected directory, but found: $target');
}
context.exit(21);
}
}
if (options.verbose) {
logger.trace('Targets:');
for (String target in options.targets) {
logger.trace(' $target');
}
}
return options;
}
static String _getSdkPath() {
return Platform.environment['DART_SDK'] ??
path.dirname(path.dirname(Platform.resolvedExecutable));
}
static void _showUsage(ArgParser parser, Logger logger,
{bool showHelpHint = true}) {
Function(String message) out = showHelpHint ? logger.stderr : logger.stdout;
// show help on stdout when showHelp is true and showHelpHint is false
out('''
Usage: $_binaryName [options...] <directory paths>
''');
out(parser.usage);
out(showHelpHint
? '''
Use --$_helpOption to display the fixes that can be specified using either
--$includeFixOption or --$excludeFixOption.'''
: '');
}
}

View File

@ -1,69 +0,0 @@
// Copyright (c) 2018, 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.
import 'dart:io' show File, Platform;
import 'package:analysis_server_client/protocol.dart';
import 'package:path/path.dart' as path;
int compareSuggestions(DartFixSuggestion s1, DartFixSuggestion s2) {
int result = s1.description.compareTo(s2.description);
if (result != 0) {
return result;
}
return (s2.location?.offset ?? 0) - (s1.location?.offset ?? 0);
}
int compareFixes(DartFix s1, DartFix s2) {
return s1.name.compareTo(s2.name);
}
/// Return the analysis_server executable by proceeding upward until finding the
/// Dart SDK repository root, then returning the analysis_server executable
/// within the repository.
///
/// Return `null` if it cannot be found.
String findServerPath() {
String pathname = Platform.script.toFilePath();
while (true) {
String parent = path.dirname(pathname);
if (parent.length >= pathname.length) {
return null;
}
String serverPath =
path.join(parent, 'pkg', 'analysis_server', 'bin', 'server.dart');
if (File(serverPath).existsSync()) {
return serverPath;
}
pathname = parent;
}
}
bool shouldShowError(AnalysisError error) {
// Only show diagnostics that will affect the fixes.
return error.type.name != 'HINT' &&
error.type.name != 'LINT' &&
error.type.name != 'TODO' &&
// TODO(danrubel): Rather than checking the error.code with
// specific strings, add something to the error indicating that
// it will be automatically fixed by edit.dartfix.
error.code != 'wrong_number_of_type_arguments_constructor';
}
String toSentenceFragment(String message) {
return message.endsWith('.')
? message.substring(0, message.length - 1)
: message;
}
String pluralize(String word, int count) => count == 1 ? word : '${word}s';
List<SourceEdit> sortEdits(SourceFileEdit sourceFileEdit) {
// Sort edits in reverse offset order.
List<SourceEdit> edits = sourceFileEdit.edits.toList();
edits.sort((a, b) {
return b.offset - a.offset;
});
return edits;
}

View File

@ -1,26 +0,0 @@
name: dartfix
version: 0.1.8
description:
A tool for migrating Dart source to newer versions of the Dart SDK
and fixing common issues.
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dartfix
environment:
sdk: '>=2.8.0 <3.0.0'
# Add the bin/dartfix.dart script to the scripts pub installs.
executables:
dartfix:
dependencies:
# Pin to an exact version of analysis_server_client because the edit.dartfix
# protocol is experimental and will continue to evolve.
analysis_server_client: '>=1.1.3 <1.1.4'
args: ^1.4.0
cli_util: ^0.2.0
path: ^1.7.0
pub_semver: ^1.4.4
dev_dependencies:
analyzer: ^0.40.0
pedantic: ^1.8.0
test: ^1.14.2

View File

@ -1,28 +0,0 @@
// Copyright (c) 2019, 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.
import 'dart:io';
import 'package:test/test.dart';
import 'test_context.dart';
void main() {
test('run original example', () async {
File exampleFile = findFile('pkg/dartfix/example/example.dart');
print('--- launching original example');
final futureResult1 =
Process.run(Platform.resolvedExecutable, [exampleFile.path]);
print('--- waiting for original example');
final result = await futureResult1;
print('--- original example output');
var text = result.stdout as String;
print(text);
expect(text.trim(), 'myDouble = 4.0');
});
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2019, 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.
import 'dart:io';
import 'package:dartfix/src/driver.dart';
import 'package:test/test.dart';
import 'test_context.dart';
void main() {
File exampleFile;
Directory exampleDir;
test('exclude fix', () async {
exampleFile = findFile('pkg/dartfix/example/example.dart');
exampleDir = exampleFile.parent;
final driver = Driver();
final testContext = TestContext();
final testLogger = TestLogger(debug: _debug);
String exampleSource = await exampleFile.readAsString();
try {
await driver.start([
if (_debug) '-v',
'--fix',
'convert_class_to_mixin',
'--excludeFix',
'convert_class_to_mixin',
exampleDir.path,
], testContext: testContext, testLogger: testLogger);
} finally {
if (_debug) {
print(testLogger.stderrBuffer.toString());
print(testLogger.stdoutBuffer.toString());
print('--- original example');
print(exampleSource);
}
}
final suggestions = driver.result.suggestions;
expect(suggestions, hasLength(0));
}, timeout: const Timeout(Duration(minutes: 3)));
}
const _debug = true;

View File

@ -1,55 +0,0 @@
// Copyright (c) 2019, 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.
import 'package:dartfix/src/driver.dart';
import 'package:dartfix/src/options.dart';
import 'package:test/test.dart';
import 'test_context.dart';
void main() {
test('help explicit', () async {
final driver = Driver();
final testContext = TestContext();
final testLogger = TestLogger();
try {
await driver.start(
['--help'], // display help and list fixes
testContext: testContext,
testLogger: testLogger,
);
fail('expected exception');
} on TestExit catch (e) {
expect(e.code, 0);
}
final errText = testLogger.stderrBuffer.toString();
final outText = testLogger.stdoutBuffer.toString();
expect(errText, isEmpty);
expect(outText, contains('--$excludeFixOption'));
expect(outText, isNot(contains('Use --help to display the fixes')));
});
test('help implicit', () async {
final driver = Driver();
final testContext = TestContext();
final testLogger = TestLogger();
try {
await driver.start(
[], // no options or arguments should display help and list fixes
testContext: testContext,
testLogger: testLogger,
);
fail('expected exception');
} on TestExit catch (e) {
expect(e.code, 0);
}
final errText = testLogger.stderrBuffer.toString();
final outText = testLogger.stdoutBuffer.toString();
print(errText);
print(outText);
expect(errText, isEmpty);
expect(outText, contains('--$excludeFixOption'));
expect(outText, isNot(contains('Use --help to display the fixes')));
});
}

View File

@ -1,47 +0,0 @@
// Copyright (c) 2019, 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.
import 'dart:io';
import 'package:dartfix/src/driver.dart';
import 'package:test/test.dart';
import 'test_context.dart';
void main() {
File exampleFile;
Directory exampleDir;
test('include fix', () async {
exampleFile = findFile('pkg/dartfix/example/example.dart');
exampleDir = exampleFile.parent;
final driver = Driver();
final testContext = TestContext();
final testLogger = TestLogger(debug: _debug);
String exampleSource = await exampleFile.readAsString();
try {
await driver.start([
if (_debug) '-v',
'--fix',
'prefer_int_literals',
exampleDir.path,
], testContext: testContext, testLogger: testLogger);
} finally {
if (_debug) {
print(testLogger.stderrBuffer.toString());
print(testLogger.stdoutBuffer.toString());
print('--- original example');
print(exampleSource);
}
}
final suggestions = driver.result.suggestions;
expect(suggestions, hasLength(1));
expectHasSuggestion(suggestions, 'Convert to an int literal');
}, timeout: const Timeout(Duration(minutes: 3)));
}
const _debug = true;

View File

@ -1,13 +0,0 @@
// Copyright (c) 2019, 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.
import 'driver_test.dart' show defineDriverTests;
void main() {
defineDriverTests(
name: 'pedantic',
options: ['--pedantic'],
expectedSuggestions: ["Replace with 'isEmpty'"],
);
}

View File

@ -1,13 +0,0 @@
// Copyright (c) 2019, 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.
import 'driver_test.dart' show defineDriverTests;
void main() {
defineDriverTests(
name: 'prefer_is_empty',
options: ['--fix', 'prefer_is_empty'],
expectedSuggestions: ["Replace with 'isEmpty'"],
);
}

View File

@ -1,124 +0,0 @@
// Copyright (c) 2018, 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.
import 'dart:io';
import 'package:analysis_server_client/protocol.dart';
import 'package:dartfix/src/driver.dart';
import 'package:test/test.dart';
import 'test_context.dart';
void main() {
defineDriverTests(
name: 'default',
options: [
'--fix',
'prefer_int_literals',
'--fix',
'convert_class_to_mixin'
],
expectedSuggestions: [
'Convert MyMixin to a mixin',
'Convert to an int literal',
],
);
}
void defineDriverTests({
String name,
List<String> options,
List<String> expectedSuggestions,
bool debug = false,
bool updateExample = false,
}) {
var fixFileName = 'example_$name.dart';
File exampleFile;
File exampleFixedFile;
Directory exampleDir;
setUp(() {
exampleFile = findFile('pkg/dartfix/example/example.dart');
exampleFixedFile = findFile('pkg/dartfix/fixed/$fixFileName');
exampleDir = exampleFile.parent;
});
test('fix example - $name', () async {
final driver = Driver();
final testContext = TestContext();
final testLogger = TestLogger(debug: debug);
String exampleSource = await exampleFile.readAsString();
await driver.start([if (debug) '-v', ...options, exampleDir.path],
testContext: testContext, testLogger: testLogger);
if (debug) {
print(testLogger.stderrBuffer.toString());
print(testLogger.stdoutBuffer.toString());
print('--- original example');
print(exampleSource);
}
expect(driver.result.edits, hasLength(1));
for (SourceEdit edit in driver.result.edits[0].edits) {
exampleSource = edit.apply(exampleSource);
}
if (debug) {
print('--- fixed example');
print(exampleSource);
}
final suggestions = driver.result.suggestions;
for (var expectedSuggestion in expectedSuggestions) {
expectHasSuggestion(suggestions, expectedSuggestion);
}
expect(suggestions, hasLength(expectedSuggestions.length));
exampleSource = replaceLeadingComment(exampleSource);
if (updateExample) {
await exampleFixedFile.writeAsString(exampleSource);
} else {
final expectedSource = await exampleFixedFile.readAsString();
expect(exampleSource, expectedSource);
}
}, timeout: const Timeout(Duration(minutes: 3)));
test('run example - $name', () async {
if (debug) print('--- launching original example');
final futureResult1 =
Process.run(Platform.resolvedExecutable, [exampleFile.path]);
if (debug) print('--- launching fixed example');
final futureResult2 =
Process.run(Platform.resolvedExecutable, [exampleFixedFile.path]);
if (debug) print('--- waiting for original example');
final result1 = await futureResult1;
if (debug) print('--- waiting for fixed example');
final result2 = await futureResult2;
final stdout1 = result1.stdout;
final stdout2 = result2.stdout;
if (debug) {
print('--- original example output');
print(stdout1);
print('--- fixed example output');
print(stdout2);
}
expect(stdout1, stdout2);
});
}
String replaceLeadingComment(String source) => source.replaceAll(
'''
// This file contains code that is modified by running dartfix.
// After running dartfix, this content matches a file in the "fixed" directory.
'''
.trim(),
'''
// This file contains code that has been modified by running dartfix.
// See example.dart for the original unmodified code.
'''
.trim());

View File

@ -1,200 +0,0 @@
// Copyright (c) 2020, 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.
import 'dart:io';
import 'package:analysis_server_client/protocol.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dartfix/src/migrate/apply.dart';
import 'package:dartfix/src/migrate/display.dart';
import 'package:test/test.dart';
void main() {
defineMigrateTests();
}
void defineMigrateTests() {
group('issue render', defineIssueRenderTests);
group('SourcePrinter', defineSourcePrinterTests);
group('applyEdits', defineApplyEditsTests);
}
void defineIssueRenderTests() {
IssueRenderer renderer;
TestLogger logger;
setUp(() {
logger = TestLogger();
renderer = IssueRenderer(logger, '.');
});
test('issue1', () {
AnalysisError issue = AnalysisError(
AnalysisErrorSeverity.ERROR,
AnalysisErrorType.COMPILE_TIME_ERROR,
Location('foo/bar/baz.dart', 1, 2, 3, 4),
'My message.',
'my_error_code',
);
renderer.render(issue);
expect(
logger.stdoutText.trim(),
contains(platformPath(
'error • My message at foo/bar/baz.dart:3:4 • (my_error_code)')),
);
expect(logger.stderrText, isEmpty);
});
test('issue2', () {
AnalysisError issue = AnalysisError(
AnalysisErrorSeverity.INFO,
AnalysisErrorType.TODO,
Location('foo/bar/qux.dart', 1, 2, 3, 4),
'todo: My message.',
'todo',
);
renderer.render(issue);
expect(
logger.stdoutText,
contains(platformPath(
'info • todo: My message at foo/bar/qux.dart:3:4 • (todo)')));
expect(logger.stderrText, isEmpty);
});
}
void defineSourcePrinterTests() {
SourcePrinter printer;
setUp(() {
printer = SourcePrinter('''
void main() {
Cat one = Cat('Tabby');
print(one);
}
class Cat {
final String name;
String color;
Cat(this.name);
String toString() {
return name?.toString() + ' is ' + color.toString();
}
}
''');
});
test('add and remove', () {
printer.insertText(192, '?');
printer.deleteRange(164, 1);
printer.insertText(98, '?');
StringBuffer buf = StringBuffer();
printer.processChangedLines((lineNumber, lineText) {
buf.writeln('$lineNumber ${lineText.trim()}');
});
expect(buf.toString().trim(), '''
8 String\x1B[7m?\x1B[0m color;
13 return name?\x1B[31m\x1B[7m.\x1B[0mtoString() + \' is \' + color\x1B[7m?\x1B[0m.toString();''');
});
}
void defineApplyEditsTests() {
test('insert', () {
String source = 'one two\nthree four';
SourceFileEdit edit = SourceFileEdit('foo.dart', 0, edits: [
SourceEdit(0, 0, 'five '),
]);
String result = applyEdits(edit, source);
expect(result, 'five one two\nthree four');
});
test('delete', () {
String source = 'one two\nthree four';
SourceFileEdit edit = SourceFileEdit('foo.dart', 0, edits: [
SourceEdit(0, 4, ''),
SourceEdit(8, 6, ''),
]);
String result = applyEdits(edit, source);
expect(result, 'two\nfour');
});
test('insert and delete', () {
String source = 'one two\nthree four';
SourceFileEdit edit = SourceFileEdit('foo.dart', 0, edits: [
SourceEdit(13, 5, ''),
SourceEdit(8, 0, 'six '),
SourceEdit(7, 1, ' '),
]);
String result = applyEdits(edit, source);
expect(result, 'one two six three');
});
}
class TestLogger implements Logger {
final bool debug;
@override
final Ansi ansi;
final stdoutBuffer = StringBuffer();
final stderrBuffer = StringBuffer();
TestLogger({this.debug = false}) : ansi = Ansi(false);
@override
void flush() {}
@override
bool get isVerbose => debug;
@override
Progress progress(String message) {
return SimpleProgress(this, message);
}
@override
void stdout(String message) {
stdoutBuffer.writeln(message);
}
@override
void write(String message) {
stdoutBuffer.write(message);
}
@override
void writeCharCode(int charCode) {
stdoutBuffer.writeCharCode(charCode);
}
@override
void stderr(String message) {
stderrBuffer.writeln(message);
}
@override
void trace(String message) {
if (debug) {
stdoutBuffer.writeln(message);
}
}
String get stdoutText => stdoutBuffer.toString();
String get stderrText => stderrBuffer.toString();
}
String platformPath(String path) {
return path.replaceAll('/', Platform.pathSeparator);
}

View File

@ -1,151 +0,0 @@
// Copyright (c) 2018, 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.
import 'package:dartfix/src/options.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'test_context.dart';
void main() {
TestContext context;
TestLogger logger;
setUp(() {
context = TestContext();
logger = TestLogger();
});
String p(String filePath) => context.convertPath(filePath);
Options parse(
List<String> args, {
String errorOut,
int exitCode,
bool force = false,
List<String> includeFixes = const <String>[],
List<String> excludeFixes = const <String>[],
bool showHelp = false,
String normalOut,
bool dependencies = false,
bool pedanticFixes = false,
bool requiredFixes = false,
bool overwrite = false,
String serverSnapshot,
List<String> targetSuffixes,
bool verbose = false,
}) {
Options options;
int actualExitCode;
try {
options = Options.parse(args, context, logger);
} on TestExit catch (e) {
actualExitCode = e.code;
}
expect(logger.stderrBuffer.toString(),
errorOut != null ? contains(errorOut) : isEmpty);
expect(logger.stdoutBuffer.toString(),
normalOut != null ? contains(normalOut) : isEmpty);
if (exitCode != null) {
expect(actualExitCode, exitCode, reason: 'exit code');
return null;
} else {
expect(actualExitCode, isNull, reason: 'exit code');
}
expect(options.force, force);
expect(options.pedanticFixes, pedanticFixes);
expect(options.overwrite, overwrite);
expect(options.serverSnapshot, serverSnapshot);
expect(options.showHelp, showHelp);
expect(options.includeFixes, includeFixes);
expect(options.excludeFixes, excludeFixes);
expect(options.verbose, verbose);
expect(path.isAbsolute(options.sdkPath), isTrue, reason: options.sdkPath);
for (String target in options.targets) {
expect(target, isNotNull);
expect(path.isAbsolute(target), isTrue, reason: '$target');
}
if (targetSuffixes != null) {
for (String suffix in targetSuffixes) {
expectContains(options.targets, suffix);
}
}
return options;
}
test('exclude fix', () {
parse(['--excludeFix', 'c', '--excludeFix', 'd', 'foo'],
excludeFixes: ['c', 'd'], targetSuffixes: ['foo']);
});
test('force', () {
parse(['--force', 'foo'], force: true, targetSuffixes: ['foo']);
});
test('help explicit', () {
parse(['--help'], normalOut: 'Display this help message', showHelp: true);
});
test('help implicit', () {
parse([], normalOut: 'Display this help message', showHelp: true);
});
test('include fix', () {
parse(['--fix', 'a', '--fix', 'b', 'foo'],
includeFixes: ['a', 'b'], targetSuffixes: ['foo']);
});
test('invalid option', () {
parse(['--foo'],
errorOut: 'Could not find an option named "foo"', exitCode: 17);
});
test('invalid option no logger', () {
try {
Options.parse(['--foo'], context, null);
fail('Expected exception');
} on TestExit catch (e) {
expect(e.code, 17, reason: 'exit code');
}
});
test('invalid target', () {
parse(['foo.dart'],
errorOut: 'Expected directory, but found', exitCode: 21);
});
test('overwrite', () {
parse(['--overwrite', 'foo'], overwrite: true, targetSuffixes: ['foo']);
});
test('pedantic fixes', () {
parse(['--pedantic', 'foo'], pedanticFixes: true);
});
test('server snapshot', () {
parse(['--server', 'some/path', 'foo'], serverSnapshot: 'some/path');
});
test('simple', () {
parse(['foo'], targetSuffixes: ['foo']);
});
test('two targets', () {
parse([p('one/foo'), p('two/bar')],
targetSuffixes: [p('one/foo'), p('two/bar')]);
});
test('verbose', () {
parse(['--verbose', 'foo'], verbose: true);
});
}
void expectContains(Iterable<String> collection, String suffix) {
for (String elem in collection) {
if (elem.endsWith(suffix)) {
return;
}
}
fail('Expected one of $collection\n to end with "$suffix"');
}

View File

@ -1,130 +0,0 @@
// Copyright (c) 2018, 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.
import 'dart:io';
import 'package:analysis_server_client/protocol.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dartfix/src/context.dart';
import 'package:test/test.dart';
class TestContext with ResourceProviderMixin implements Context {
@override
String get workingDir => convertPath('/usr/some/non/existing/directory');
@override
bool exists(String filePath) => true;
@override
void exit(int code) {
throw TestExit(code);
}
@override
bool isDirectory(String filePath) => !filePath.endsWith('.dart');
}
class TestExit {
final int code;
TestExit(this.code);
@override
String toString() => 'TestExit($code)';
}
class TestLogger implements Logger {
final bool debug;
@override
final Ansi ansi;
final stdoutBuffer = StringBuffer();
final stderrBuffer = StringBuffer();
TestLogger({this.debug = false}) : ansi = Ansi(false);
@override
void flush() {}
@override
bool get isVerbose => debug;
@override
Progress progress(String message) {
return SimpleProgress(this, message);
}
@override
void stderr(String message) {
stderrBuffer.writeln(message);
}
@override
void stdout(String message) {
stdoutBuffer.writeln(message);
}
@override
void write(String message) {
stdoutBuffer.write(message);
}
@override
void writeCharCode(int charCode) {
stdoutBuffer.writeCharCode(charCode);
}
@override
void trace(String message) {
if (debug) {
stdoutBuffer.writeln(message);
}
}
}
void expectHasSuggestion(
List<DartFixSuggestion> suggestions, String expectedText) {
for (DartFixSuggestion suggestion in suggestions) {
if (suggestion.description.contains(expectedText)) {
return;
}
}
fail('Failed to find suggestion containing: $expectedText\n'
'in ${suggestions.map((s) => s.description).toList()}');
}
void expectDoesNotHaveSuggestion(
List<DartFixSuggestion> suggestions, String expectedText) {
for (DartFixSuggestion suggestion in suggestions) {
if (suggestion.description.contains(expectedText)) {
fail('Did not expect to find suggestion containing: $expectedText');
}
}
}
File findFile(String relPath) {
Directory dir = Directory.current;
while (true) {
final file = File.fromUri(dir.uri.resolve(relPath));
if (file.existsSync()) {
return file;
}
final parent = dir.parent;
if (parent.path == dir.path) {
fail('Failed to find $relPath');
}
dir = parent;
}
}
String findValue(File pubspec, String key) {
List<String> lines = pubspec.readAsLinesSync();
for (String line in lines) {
if (line.trim().startsWith('$key:')) {
return line.split(':')[1].trim();
}
}
fail('Failed to find $key in ${pubspec.path}');
}

View File

@ -1,27 +0,0 @@
// Copyright (c) 2018, 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.
import 'package:test/test.dart';
import 'src/driver_example_test.dart' as driver_example;
import 'src/driver_exclude_test.dart' as driver_exclude;
import 'src/driver_help_test.dart' as driver_help;
import 'src/driver_include_test.dart' as driver_include;
import 'src/driver_pedantic_test.dart' as driver_pedantic;
import 'src/driver_prefer_is_empty_test.dart' as driver_prefer_is_empty;
import 'src/driver_test.dart' as driver;
import 'src/migrate_command_test.dart' as migrate_command_test;
import 'src/options_test.dart' as options_test;
void main() {
group('driver', driver_example.main);
group('driver', driver_exclude.main);
group('driver', driver_help.main);
group('driver', driver_include.main);
group('driver', driver_pedantic.main);
group('driver', driver_prefer_is_empty.main);
group('driver', driver.main);
group('migrate', migrate_command_test.main);
group('options', options_test.main);
}