Introduce function `parseFile2`.
This is similar to the existing `parseFile` function, but it is more
efficient, and it supports the same parameters `featureSet` and
`throwIfDiagnostics` as `parseString`.
Change-Id: I4b9ecc36a137cc1c99e671e379f00afa3942c9ba
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105542
Commit-Queue: Paul Berry <paulberry@google.com>
Auto-Submit: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index f0d1b34a..adc4328 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -21,6 +21,9 @@
* Changed the return type of `ClassTypeAlias.declaredElement` to `ClassElement`.
There is no functional change; it has always returned an instance of
`ClassElement`.
+* Deprecated `parseFile`. Please use `parseFile2` instead--in addition to
+ supporting the same `featureSet` and `throwIfDiagnostics` parameters as
+ `parseString`, it is much more efficient than `parseFile`.
## 0.36.3
* Deprecated `AstFactory.compilationUnit`. In a future analyzer release, this
diff --git a/pkg/analyzer/lib/dart/analysis/utilities.dart b/pkg/analyzer/lib/dart/analysis/utilities.dart
index 7b96945..820ffa1 100644
--- a/pkg/analyzer/lib/dart/analysis/utilities.dart
+++ b/pkg/analyzer/lib/dart/analysis/utilities.dart
@@ -26,6 +26,9 @@
/// Note that if more than one file is going to be parsed then this function is
/// inefficient. Clients should instead use [AnalysisContextCollection] to
/// create one or more contexts and use those contexts to parse the files.
+///
+/// Deprecated: please use [parseFile2] instead.
+@Deprecated('Use parseFile2')
ParsedUnitResult parseFile(
{@required String path, ResourceProvider resourceProvider}) {
AnalysisContext context =
@@ -33,6 +36,42 @@
return context.currentSession.getParsedUnit(path);
}
+/// Return the result of parsing the file at the given [path].
+///
+/// If a [resourceProvider] is given, it will be used to access the file system.
+///
+/// [featureSet] determines what set of features will be assumed by the parser.
+/// This parameter is required because the analyzer does not yet have a
+/// performant way of computing the correct feature set for a single file to be
+/// parsed. Callers that need the feature set to be strictly correct must
+/// create an [AnalysisContextCollection], query it to get an [AnalysisContext],
+/// query it to get an [AnalysisSession], and then call `getParsedUnit`.
+///
+/// Callers that don't need the feature set to be strictly correct can pass in
+/// `FeatureSet.fromEnableFlags([])` to enable the default set of features; this
+/// is much more performant than using an analysis session, because it doesn't
+/// require the analyzer to process the SDK.
+///
+/// If [throwIfDiagnostics] is `true` (the default), then if any diagnostics are
+/// produced because of syntactic errors in the [content] an `ArgumentError`
+/// will be thrown. If the parameter is `false`, then the caller can check the
+/// result to see whether there are any errors.
+ParseStringResult parseFile2(
+ {@required String path,
+ ResourceProvider resourceProvider,
+ @required FeatureSet featureSet,
+ bool throwIfDiagnostics: true}) {
+ if (featureSet == null) {
+ throw ArgumentError('A non-null feature set must be provided.');
+ }
+ resourceProvider ??= PhysicalResourceProvider.INSTANCE;
+ var content = (resourceProvider.getResource(path) as File).readAsStringSync();
+ return parseString(
+ content: content,
+ featureSet: featureSet,
+ throwIfDiagnostics: throwIfDiagnostics);
+}
+
/// Returns the result of parsing the given [content] as a compilation unit.
///
/// If a [featureSet] is provided, it will be the default set of features that
diff --git a/pkg/analyzer/test/dart/analysis/utilities_test.dart b/pkg/analyzer/test/dart/analysis/utilities_test.dart
index 1aebc1fe8..89c3dee 100644
--- a/pkg/analyzer/test/dart/analysis/utilities_test.dart
+++ b/pkg/analyzer/test/dart/analysis/utilities_test.dart
@@ -2,10 +2,14 @@
// 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:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
+import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
+import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -17,6 +21,109 @@
@reflectiveTest
class UtilitiesTest with ResourceProviderMixin {
+ FeatureSet get defaultFeatureSet =>
+ FeatureSet.forTesting(sdkVersion: '2.2.2');
+
+ test_parseFile_default_resource_provider() {
+ String content = '''
+void main() => print('Hello, world!');
+ ''';
+ ParseStringResult result = _withTemporaryFile(content,
+ (path) => parseFile2(path: path, featureSet: defaultFeatureSet));
+ expect(result.content, content);
+ expect(result.errors, isEmpty);
+ expect(result.lineInfo, isNotNull);
+ expect(result.unit.toString(),
+ equals("void main() => print('Hello, world!');"));
+ }
+
+ test_parseFile_errors_noThrow() {
+ String content = '''
+void main() => print('Hello, world!')
+''';
+ ParseStringResult result = _withMemoryFile(
+ content,
+ (resourceProvider, path) => parseFile2(
+ path: path,
+ featureSet: defaultFeatureSet,
+ resourceProvider: resourceProvider,
+ throwIfDiagnostics: false));
+ expect(result.content, content);
+ expect(result.errors, hasLength(1));
+ expect(result.lineInfo, isNotNull);
+ expect(result.unit.toString(),
+ equals("void main() => print('Hello, world!');"));
+ }
+
+ test_parseFile_errors_throw() {
+ String content = '''
+void main() => print('Hello, world!')
+''';
+ expect(
+ () => _withMemoryFile(
+ content,
+ (resourceProvider, path) => parseFile2(
+ path: path,
+ featureSet: defaultFeatureSet,
+ resourceProvider: resourceProvider)),
+ throwsA(const TypeMatcher<ArgumentError>()));
+ }
+
+ test_parseFile_featureSet_nnbd_off() {
+ String content = '''
+int? f() => 1;
+''';
+ var featureSet = FeatureSet.forTesting(sdkVersion: '2.3.0');
+ expect(featureSet.isEnabled(Feature.non_nullable), isFalse);
+ ParseStringResult result = _withMemoryFile(
+ content,
+ (resourceProvider, path) => parseFile2(
+ path: path,
+ resourceProvider: resourceProvider,
+ throwIfDiagnostics: false,
+ featureSet: featureSet));
+ expect(result.content, content);
+ expect(result.errors, hasLength(1));
+ expect(result.lineInfo, isNotNull);
+ expect(result.unit.toString(), equals('int? f() => 1;'));
+ }
+
+ test_parseFile_featureSet_nnbd_on() {
+ String content = '''
+int? f() => 1;
+''';
+ var featureSet =
+ FeatureSet.forTesting(additionalFeatures: [Feature.non_nullable]);
+ ParseStringResult result = _withMemoryFile(
+ content,
+ (resourceProvider, path) => parseFile2(
+ path: path,
+ resourceProvider: resourceProvider,
+ throwIfDiagnostics: false,
+ featureSet: featureSet));
+ expect(result.content, content);
+ expect(result.errors, isEmpty);
+ expect(result.lineInfo, isNotNull);
+ expect(result.unit.toString(), equals('int? f() => 1;'));
+ }
+
+ test_parseFile_noErrors() {
+ String content = '''
+void main() => print('Hello, world!');
+''';
+ ParseStringResult result = _withMemoryFile(
+ content,
+ (resourceProvider, path) => parseFile2(
+ path: path,
+ featureSet: defaultFeatureSet,
+ resourceProvider: resourceProvider));
+ expect(result.content, content);
+ expect(result.errors, isEmpty);
+ expect(result.lineInfo, isNotNull);
+ expect(result.unit.toString(),
+ equals("void main() => print('Hello, world!');"));
+ }
+
test_parseString_errors_noThrow() {
String content = '''
void main() => print('Hello, world!')
@@ -88,4 +195,24 @@
expect(result.unit.toString(),
equals("void main() => print('Hello, world!');"));
}
+
+ T _withMemoryFile<T>(String content,
+ T callback(MemoryResourceProvider resourceProvider, String path)) {
+ var resourceProvider = MemoryResourceProvider();
+ var path =
+ resourceProvider.pathContext.fromUri(Uri.parse('file:///test.dart'));
+ resourceProvider.newFile(path, content);
+ return callback(resourceProvider, path);
+ }
+
+ T _withTemporaryFile<T>(String content, T callback(String path)) {
+ var tempDir = Directory.systemTemp.createTempSync();
+ try {
+ var file = File(p.join(tempDir.path, 'test.dart'));
+ file.writeAsStringSync(content);
+ return callback(file.path);
+ } finally {
+ tempDir.deleteSync(recursive: true);
+ }
+ }
}