Version 2.13.0-57.0.dev
Merge commit 'c2195fcc14f3ad00d9b618767bacc8e3ad956b27' into 'dev'
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 3f89245..df459d1 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -47,8 +47,6 @@
import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
-import 'package:analyzer/src/context/builder.dart';
-import 'package:analyzer/src/context/context_root.dart';
import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
import 'package:analyzer/src/dart/analysis/status.dart' as nd;
import 'package:analyzer/src/generated/engine.dart';
@@ -198,6 +196,10 @@
return _onAnalysisStartedController.stream;
}
+ String get sdkPath {
+ return sdkManager.defaultSdkDirectory;
+ }
+
/// The socket from which requests are being read has been closed.
void done() {}
@@ -205,17 +207,6 @@
/// read.
void error(argument) {}
- /// Return one of the SDKs that has been created, or `null` if no SDKs have
- /// been created yet.
- DartSdk findSdk() {
- var sdk = sdkManager.anySdk;
- if (sdk != null) {
- return sdk;
- }
- // TODO(brianwilkerson) Should we create an SDK using the default options?
- return null;
- }
-
/// Return the cached analysis result for the file with the given [path].
/// If there is no cached result, return `null`.
ResolvedUnitResult getCachedResolvedUnit(String path) {
@@ -388,7 +379,6 @@
/// projects/contexts support.
void setAnalysisRoots(String requestId, List<String> includedPaths,
List<String> excludedPaths) {
- declarationsTracker?.discardContexts();
if (notificationManager != null) {
notificationManager.setAnalysisRoots(includedPaths, excludedPaths);
}
@@ -397,7 +387,6 @@
} on UnimplementedError catch (e) {
throw RequestFailure(Response.unsupportedFeature(requestId, e.message));
}
- addContextsToDeclarationsTracker();
analysisDriverScheduler.transitionToAnalyzingToIdleIfNoFilesToAnalyze();
}
@@ -408,29 +397,13 @@
notificationManager.setSubscriptions(subscriptions);
}
analysisServices = subscriptions;
- var allNewFiles = subscriptions.values.expand((files) => files).toSet();
- for (var file in allNewFiles) {
- // The result will be produced by the "results" stream with
- // the fully resolved unit, and processed with sending analysis
- // notifications as it happens after content changes.
- if (AnalysisEngine.isDartFileName(file)) {
- getResolvedUnit(file, sendCachedToStream: true);
- }
- }
+ _sendSubscriptions(analysis: true);
}
/// Implementation for `flutter.setSubscriptions`.
void setFlutterSubscriptions(Map<FlutterService, Set<String>> subscriptions) {
flutterServices = subscriptions;
- var allNewFiles = subscriptions.values.expand((files) => files).toSet();
- for (var file in allNewFiles) {
- // The result will be produced by the "results" stream with
- // the fully resolved unit, and processed with sending analysis
- // notifications as it happens after content changes.
- if (AnalysisEngine.isDartFileName(file)) {
- getResolvedUnit(file, sendCachedToStream: true);
- }
- }
+ _sendSubscriptions(flutter: true);
}
/// Implementation for `analysis.setGeneralSubscriptions`.
@@ -467,8 +440,7 @@
// during normal analysis (for example dot folders are skipped over in
// _handleWatchEventImpl).
return contextManager.isInAnalysisRoot(file) &&
- !contextManager.isContainedInDotFolder(file) &&
- !contextManager.isIgnored(file);
+ !contextManager.isContainedInDotFolder(file);
}
Future<void> shutdown() {
@@ -596,6 +568,31 @@
scheduleImplementedNotification(this, files);
}
}
+
+ void _sendSubscriptions({bool analysis = false, bool flutter = false}) {
+ var files = <String>{};
+
+ if (analysis) {
+ analysisServices.values.forEach((serviceFiles) {
+ files.addAll(serviceFiles);
+ });
+ }
+
+ if (flutter) {
+ flutterServices.values.forEach((serviceFiles) {
+ files.addAll(serviceFiles);
+ });
+ }
+
+ for (var file in files) {
+ // The result will be produced by the "results" stream with
+ // the fully resolved unit, and processed with sending analysis
+ // notifications as it happens after content changes.
+ if (AnalysisEngine.isDartFileName(file)) {
+ getResolvedUnit(file, sendCachedToStream: true);
+ }
+ }
+ }
}
/// Various IDE options.
@@ -642,14 +639,30 @@
analysisServer.notificationManager;
@override
- nd.AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot) {
- var builder = createContextBuilder(folder);
- var workspace = ContextBuilder.createWorkspace(
- resourceProvider: resourceProvider,
- options: builder.builderOptions,
- rootPath: folder.path,
- );
- var analysisDriver = builder.buildDriver(contextRoot, workspace);
+ void afterContextsCreated() {
+ analysisServer.addContextsToDeclarationsTracker();
+ analysisServer._sendSubscriptions(analysis: true, flutter: true);
+ }
+
+ @override
+ void afterWatchEvent(WatchEvent event) {
+ analysisServer._onAnalysisSetChangedController.add(null);
+ }
+
+ @override
+ void applyFileRemoved(String file) {
+ sendAnalysisNotificationFlushResults(analysisServer, [file]);
+ }
+
+ @override
+ void broadcastWatchEvent(WatchEvent event) {
+ analysisServer.notifyDeclarationsTracker(event.path);
+ analysisServer.notifyFlutterWidgetDescriptions(event.path);
+ analysisServer.pluginManager.broadcastWatchEvent(event);
+ }
+
+ @override
+ void listenAnalysisDriver(nd.AnalysisDriver analysisDriver) {
analysisDriver.results.listen((result) {
var notificationManager = analysisServer.notificationManager;
var path = result.path;
@@ -757,66 +770,6 @@
});
analysisDriver.exceptions.listen(analysisServer.logExceptionResult);
analysisDriver.priorityFiles = analysisServer.priorityFiles.toList();
- analysisServer.driverMap[folder] = analysisDriver;
- return analysisDriver;
- }
-
- @override
- void afterContextRefresh() {
- analysisServer.addContextsToDeclarationsTracker();
- }
-
- @override
- void afterWatchEvent(WatchEvent event) {
- analysisServer._onAnalysisSetChangedController.add(null);
- }
-
- @override
- void analysisOptionsUpdated(nd.AnalysisDriver driver) {
- analysisServer.updateContextInDeclarationsTracker(driver);
- }
-
- @override
- void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
- var analysisDriver = analysisServer.driverMap[contextFolder];
- if (analysisDriver != null) {
- changeSet.addedFiles.forEach((path) {
- analysisDriver.addFile(path);
- });
- changeSet.changedFiles.forEach((path) {
- analysisDriver.changeFile(path);
- });
- changeSet.removedFiles.forEach((path) {
- analysisDriver.removeFile(path);
- });
- }
- }
-
- @override
- void applyFileRemoved(nd.AnalysisDriver driver, String file) {
- driver.removeFile(file);
- sendAnalysisNotificationFlushResults(analysisServer, [file]);
- }
-
- @override
- void broadcastWatchEvent(WatchEvent event) {
- analysisServer.notifyDeclarationsTracker(event.path);
- analysisServer.notifyFlutterWidgetDescriptions(event.path);
- analysisServer.pluginManager.broadcastWatchEvent(event);
- }
-
- @override
- ContextBuilder createContextBuilder(Folder folder) {
- var builderOptions = ContextBuilderOptions();
- var builder = ContextBuilder(
- resourceProvider, analysisServer.sdkManager, null,
- options: builderOptions);
- builder.analysisDriverScheduler = analysisServer.analysisDriverScheduler;
- builder.performanceLog = analysisServer.analysisPerformanceLogger;
- builder.byteStore = analysisServer.byteStore;
- builder.enableIndex = true;
- builder.lookForBazelBuildFileSubstitutes = false;
- return builder;
}
@override
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index a5744ac..7d4584e 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -177,8 +177,15 @@
CompletionLibrariesWorker(declarationsTracker);
}
- contextManager = ContextManagerImpl(resourceProvider, sdkManager,
- analyzedFilesGlobs, instrumentationService);
+ contextManager = ContextManagerImpl(
+ resourceProvider,
+ sdkManager,
+ byteStore,
+ analysisPerformanceLogger,
+ analysisDriverScheduler,
+ analyzedFilesGlobs,
+ instrumentationService,
+ );
searchEngine = SearchEngineImpl(driverMap.values);
}
@@ -217,6 +224,7 @@
}
void addContextsToDeclarationsTracker() {
+ declarationsTracker?.discardContexts();
for (var driver in driverMap.values) {
declarationsTracker?.addContext(driver.analysisContext);
driver.resetUriResolution();
@@ -409,11 +417,6 @@
bool fatal = false,
});
- void updateContextInDeclarationsTracker(nd.AnalysisDriver driver) {
- declarationsTracker?.discardContext(driver.analysisContext);
- declarationsTracker?.addContext(driver.analysisContext);
- }
-
/// Return the path to the location of the byte store on disk, or `null` if
/// there is no on-disk byte store.
String _getByteStorePath() {
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index fbca822..4430ee5 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -5,17 +5,18 @@
import 'dart:async';
import 'dart:collection';
import 'dart:core';
+import 'dart:io';
import 'package:analysis_server/src/plugin/notification_manager.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_parser.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
-import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
-import 'package:analyzer/src/context/builder.dart';
-import 'package:analyzer/src/context/context_root.dart';
-import 'package:analyzer/src/context/packages.dart';
+import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
+import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
@@ -25,11 +26,8 @@
import 'package:analyzer/src/lint/pub.dart';
import 'package:analyzer/src/manifest/manifest_validator.dart';
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
-import 'package:analyzer/src/source/package_map_resolver.dart';
-import 'package:analyzer/src/source/path_filter.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:analyzer/src/util/glob.dart';
-import 'package:analyzer/src/util/yaml.dart';
import 'package:analyzer/src/workspace/bazel.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
@@ -45,153 +43,6 @@
/// Not private to enable testing.
var experimentalEnableBazelWatching = false;
-/// An indication of which files have been added, changed, removed, or deleted.
-///
-/// No file should be added to the change set more than once, either with the
-/// same or a different kind of change. It does not make sense, for example,
-/// for a file to be both added and removed.
-class ChangeSet {
- /// A list containing paths of added files.
- final List<String> addedFiles = [];
-
- /// A list containing paths of changed files.
- final List<String> changedFiles = [];
-
- /// A list containing paths of removed files.
- final List<String> removedFiles = [];
-
- /// Record that the file with the specified [path] has been added.
- void addedSource(String path) {
- addedFiles.add(path);
- }
-
- /// Record that the file with the specified [path] has been removed.
- void removedSource(String path) {
- removedFiles.add(path);
- }
-}
-
-/// Information tracked by the [ContextManager] for each context.
-class ContextInfo {
- /// The [Folder] for which this information object is created.
- final Folder folder;
-
- /// The [PathFilter] used to filter sources from being analyzed.
- final PathFilter pathFilter;
-
- /// The enclosed pubspec-based contexts.
- final List<ContextInfo> children = <ContextInfo>[];
-
- /// The [ContextInfo] that encloses this one, or `null` if this is the virtual
- /// [ContextInfo] object that acts as the ancestor of all other [ContextInfo]
- /// objects.
- ContextInfo parent;
-
- /// The package description file path for this context.
- String packageDescriptionPath;
-
- /// The folder disposition for this context.
- final FolderDisposition disposition;
-
- /// Paths to files which determine the folder disposition and package map.
- ///
- /// TODO(paulberry): if any of these files are outside of [folder], they won't
- /// be watched for changes. I believe the use case for watching these files
- /// is no longer relevant.
- Set<String> _dependencies = <String>{};
-
- /// The analysis driver that was created for the [folder].
- AnalysisDriver analysisDriver;
-
- /// Map from full path to the [Source] object, for each source that has been
- /// added to the context.
- Map<String, Source> sources = HashMap<String, Source>();
-
- ContextInfo(ContextManagerImpl contextManager, this.parent, this.folder,
- File packagespecFile, this.disposition)
- : pathFilter = PathFilter(
- folder.path, null, contextManager.resourceProvider.pathContext) {
- packageDescriptionPath = packagespecFile.path;
- parent.children.add(this);
- }
-
- /// Create the virtual [ContextInfo] which acts as an ancestor to all other
- /// [ContextInfo]s.
- ContextInfo._root()
- : folder = null,
- pathFilter = null,
- disposition = null;
-
- /// Iterate through all [children] and their children, recursively.
- Iterable<ContextInfo> get descendants sync* {
- for (var child in children) {
- yield child;
- yield* child.descendants;
- }
- }
-
- /// Returns `true` if this is a "top level" context, meaning that the folder
- /// associated with it is not contained within any other folders that have an
- /// associated context.
- bool get isTopLevel => parent.parent == null;
-
- /// Returns `true` if [path] is excluded, as it is in one of the children.
- bool excludes(String path) {
- return children.any((child) {
- return child.folder.contains(path);
- });
- }
-
- /// Returns `true` if [resource] is excluded, as it is in one of the children.
- bool excludesResource(Resource resource) => excludes(resource.path);
-
- /// Return the first [ContextInfo] in [children] whose associated folder is or
- /// contains [path]. If there is no such [ContextInfo], return `null`.
- ContextInfo findChildInfoFor(String path) {
- for (var info in children) {
- if (info.folder.isOrContains(path)) {
- return info;
- }
- }
- return null;
- }
-
- /// Determine if the given [path] is one of the dependencies most recently
- /// passed to [setDependencies].
- bool hasDependency(String path) => _dependencies.contains(path);
-
- /// Returns `true` if [path] should be ignored.
- bool ignored(String path) => pathFilter.ignored(path);
-
- /// Update the set of dependencies for this context.
- void setDependencies(Iterable<String> newDependencies) {
- _dependencies = newDependencies.toSet();
- }
-
- /// Return `true` if the given [path] is managed by this context or by
- /// any of its children.
- bool _managesOrHasChildThatManages(String path) {
- if (parent == null) {
- for (var child in children) {
- if (child._managesOrHasChildThatManages(path)) {
- return true;
- }
- }
- return false;
- } else {
- if (!folder.isOrContains(path)) {
- return false;
- }
- for (var child in children) {
- if (child._managesOrHasChildThatManages(path)) {
- return true;
- }
- }
- return !pathFilter.ignored(path);
- }
- }
-}
-
/// Class that maintains a mapping from included/excluded paths to a set of
/// folders that should correspond to analysis contexts.
abstract class ContextManager {
@@ -224,16 +75,10 @@
/// to [setRoots].
List<String> get includedPaths;
- /// Like [getDriverFor], but returns the [Folder] which allows plugins to
- /// create & manage their own tree of drivers just like using [getDriverFor].
- ///
- /// This folder should be the root of analysis context, not just the
- /// containing folder of the path (like basename), as this is NOT just a file
- /// API.
- ///
- /// This exists at least temporarily, for plugin support until the new API is
- /// ready.
- Folder getContextFolderFor(String path);
+ /// Return the existing analysis context that should be used to analyze the
+ /// given [path], or `null` if the [path] is not analyzed in any of the
+ /// created analysis contexts.
+ DriverBasedAnalysisContext getContextFor(String path);
/// Return the [AnalysisDriver] for the "innermost" context whose associated
/// folder is or contains the given path. ("innermost" refers to the nesting
@@ -244,32 +89,19 @@
/// If no driver contains the given path, `null` is returned.
AnalysisDriver getDriverFor(String path);
- /// Return a list of all of the analysis drivers reachable from the given
- /// [analysisRoot] (the driver associated with [analysisRoot] and all of its
- /// descendants).
- List<AnalysisDriver> getDriversInAnalysisRoot(Folder analysisRoot);
-
/// Determine whether the given [path], when interpreted relative to innermost
/// context root, contains a folder whose name starts with '.'.
+ ///
+ /// TODO(scheglov) Remove it, just [isInAnalysisRoot] should be enough.
bool isContainedInDotFolder(String path);
- /// Return `true` if the given [path] is ignored by a [ContextInfo] whose
- /// folder contains it.
- bool isIgnored(String path);
-
/// Return `true` if the given absolute [path] is in one of the current
/// root folders and is not excluded.
bool isInAnalysisRoot(String path);
- /// Return the number of contexts reachable from the given [analysisRoot] (the
- /// context associated with [analysisRoot] and all of its descendants).
- int numberOfContextsInAnalysisRoot(Folder analysisRoot);
-
/// Rebuild the set of contexts from scratch based on the data last sent to
- /// [setRoots]. Only contexts contained in the given list of analysis [roots]
- /// will be rebuilt, unless the list is `null`, in which case every context
- /// will be rebuilt.
- void refresh(List<Resource> roots);
+ /// [setRoots].
+ void refresh();
/// Change the set of paths which should be used as starting points to
/// determine the context directories.
@@ -288,34 +120,23 @@
/// Return the notification manager associated with the server.
AbstractNotificationManager get notificationManager;
- /// Create and return a new analysis driver rooted at the given [folder].
- AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot);
-
/// Called after contexts are rebuilt, such as after recovering from a watcher
/// failure.
- void afterContextRefresh();
+ void afterContextsCreated();
/// An [event] was processed, so analysis state might be different now.
void afterWatchEvent(WatchEvent event);
- /// Called when analysis options or URI resolution in the [driver] are
- /// changed.
- void analysisOptionsUpdated(AnalysisDriver driver);
-
- /// Called when the set of files associated with a context have changed (or
- /// some of those files have been modified). [changeSet] is the set of
- /// changes that need to be applied to the context.
- void applyChangesToContext(Folder contextFolder, ChangeSet changeSet);
-
- /// The given [file] was removed from the folder analyzed in the [driver].
- void applyFileRemoved(AnalysisDriver driver, String file);
+ /// The given [file] was removed.
+ void applyFileRemoved(String file);
/// Sent the given watch [event] to any interested plugins.
void broadcastWatchEvent(WatchEvent event);
- /// Create and return a context builder that can be used to create a context
- /// for the files in the given [folder].
- ContextBuilder createContextBuilder(Folder folder);
+ /// Add listeners to the [driver]. This must be the only listener.
+ ///
+ /// TODO(scheglov) Just pass results in here?
+ void listenAnalysisDriver(AnalysisDriver driver);
/// Remove the context associated with the given [folder]. [flushedFiles] is
/// a list of the files which will be "orphaned" by removing this context
@@ -348,6 +169,18 @@
/// particular context.
final DartSdkManager sdkManager;
+ /// The storage for cached results.
+ final ByteStore _byteStore;
+
+ /// The logger used to create analysis contexts.
+ final PerformanceLog _performanceLog;
+
+ /// The scheduler used to create analysis contexts, and report status.
+ final AnalysisDriverScheduler _scheduler;
+
+ /// The current set of analysis contexts.
+ AnalysisContextCollectionImpl _collection;
+
/// The context used to work with file system paths.
pathos.Context pathContext;
@@ -370,10 +203,6 @@
@override
ContextManagerCallbacks callbacks;
- /// Virtual [ContextInfo] which acts as the ancestor of all other
- /// [ContextInfo]s.
- final ContextInfo rootInfo = ContextInfo._root();
-
@override
final Map<Folder, AnalysisDriver> driverMap =
HashMap<Folder, AnalysisDriver>();
@@ -391,6 +220,9 @@
ContextManagerImpl(
this.resourceProvider,
this.sdkManager,
+ this._byteStore,
+ this._performanceLog,
+ this._scheduler,
this.analyzedFilesGlobs,
this._instrumentationService,
) {
@@ -398,313 +230,50 @@
}
@override
- Folder getContextFolderFor(String path) {
- return _getInnermostContextInfoFor(path)?.folder;
- }
-
- /// For testing: get the [ContextInfo] object for the given [folder], if any.
- ContextInfo getContextInfoFor(Folder folder) {
- var info = _getInnermostContextInfoFor(folder.path);
- if (info != null && folder == info.folder) {
- return info;
+ DriverBasedAnalysisContext getContextFor(String path) {
+ try {
+ return _collection?.contextFor(path);
+ } on StateError {
+ return null;
}
- return null;
}
@override
AnalysisDriver getDriverFor(String path) {
- return _getInnermostContextInfoFor(path)?.analysisDriver;
- }
-
- @override
- List<AnalysisDriver> getDriversInAnalysisRoot(Folder analysisRoot) {
- var drivers = <AnalysisDriver>[];
- void addContextAndDescendants(ContextInfo info) {
- drivers.add(info.analysisDriver);
- info.children.forEach(addContextAndDescendants);
- }
-
- var innermostContainingInfo =
- _getInnermostContextInfoFor(analysisRoot.path);
- if (innermostContainingInfo != null) {
- if (analysisRoot == innermostContainingInfo.folder) {
- addContextAndDescendants(innermostContainingInfo);
- } else {
- for (var info in innermostContainingInfo.children) {
- if (analysisRoot.isOrContains(info.folder.path)) {
- addContextAndDescendants(info);
- }
- }
- }
- }
- return drivers;
+ return getContextFor(path)?.driver;
}
/// Determine whether the given [path], when interpreted relative to innermost
/// context root, contains a folder whose name starts with '.'.
@override
bool isContainedInDotFolder(String path) {
- var info = _getInnermostContextInfoFor(path);
- return info != null && _isContainedInDotFolder(info.folder.path, path);
- }
-
- @override
- bool isIgnored(String path) {
- var info = rootInfo;
- do {
- info = info.findChildInfoFor(path);
- if (info == null) {
- return false;
- }
- if (info.ignored(path)) {
- return true;
- }
- } while (true);
- }
-
- @override
- bool isInAnalysisRoot(String path) {
- // check if excluded
- if (_isExcluded(path)) {
- return false;
- }
- // check if in one of the roots
- for (var info in rootInfo.children) {
- if (info.folder.contains(path)) {
+ for (var analysisContext in _collection.contexts) {
+ var contextImpl = analysisContext as DriverBasedAnalysisContext;
+ if (_isContainedInDotFolder(contextImpl.contextRoot.root.path, path)) {
return true;
}
}
- // no
return false;
}
@override
- int numberOfContextsInAnalysisRoot(Folder analysisRoot) {
- var count = 0;
- void addContextAndDescendants(ContextInfo info) {
- count++;
- info.children.forEach(addContextAndDescendants);
- }
-
- var innermostContainingInfo =
- _getInnermostContextInfoFor(analysisRoot.path);
- if (innermostContainingInfo != null) {
- if (analysisRoot == innermostContainingInfo.folder) {
- addContextAndDescendants(innermostContainingInfo);
- } else {
- for (var info in innermostContainingInfo.children) {
- if (analysisRoot.isOrContains(info.folder.path)) {
- addContextAndDescendants(info);
- }
- }
- }
- }
- return count;
- }
-
- /// Process [options] for the given context [info].
- void processOptionsForDriver(
- ContextInfo info, AnalysisOptionsImpl analysisOptions, YamlMap options) {
- if (options == null) {
- return;
- }
-
- // Check for embedded options.
- var embeddedOptions = _getEmbeddedOptions(info);
- if (embeddedOptions != null) {
- options = Merger().merge(embeddedOptions, options);
- }
-
- applyToAnalysisOptions(analysisOptions, options);
-
- if (analysisOptions.excludePatterns != null) {
- // Set ignore patterns.
- setIgnorePatternsForContext(info, analysisOptions.excludePatterns);
- }
+ bool isInAnalysisRoot(String path) {
+ return _collection.contexts.any(
+ (context) => context.contextRoot.isAnalyzed(path),
+ );
}
@override
- void refresh(List<Resource> roots) {
- // Destroy old contexts
- var contextInfos = rootInfo.descendants.toList();
- if (roots == null) {
- contextInfos.forEach(_destroyContext);
- } else {
- roots.forEach((Resource resource) {
- contextInfos.forEach((ContextInfo contextInfo) {
- if (resource is Folder &&
- resource.isOrContains(contextInfo.folder.path)) {
- _destroyContext(contextInfo);
- }
- });
- });
- }
-
- // Rebuild contexts based on the data last sent to setRoots().
- setRoots(includedPaths, excludedPaths);
-
- callbacks.afterContextRefresh();
- }
-
- /// Sets the [ignorePatterns] for the context having info [info].
- void setIgnorePatternsForContext(
- ContextInfo info, List<String> ignorePatterns) {
- info.pathFilter.setIgnorePatterns(ignorePatterns);
+ void refresh() {
+ _createAnalysisContexts();
}
@override
void setRoots(List<String> includedPaths, List<String> excludedPaths) {
- var contextInfos = rootInfo.descendants.toList();
- // included
- var includedFolders = <Folder>[];
- {
- // Sort paths to ensure that outer roots are handled before inner roots,
- // so we can correctly ignore inner roots, which are already managed
- // by outer roots.
- var uniqueIncludedPaths = LinkedHashSet<String>.from(includedPaths);
- var sortedIncludedPaths = uniqueIncludedPaths.toList();
- sortedIncludedPaths.sort((a, b) => a.length - b.length);
- // Convert paths to folders.
- for (var path in sortedIncludedPaths) {
- var resource = resourceProvider.getResource(path);
- if (resource is Folder) {
- includedFolders.add(resource);
- } else if (!resource.exists) {
- // Non-existent resources are ignored. TODO(paulberry): we should set
- // up a watcher to ensure that if the resource appears later, we will
- // begin analyzing it.
- } else {
- // TODO(scheglov) implemented separate files analysis
- throw UnimplementedError('$path is not a folder. '
- 'Only support for folder analysis is implemented currently.');
- }
- }
- }
this.includedPaths = includedPaths;
- // excluded
- var oldExcludedPaths = this.excludedPaths;
this.excludedPaths = excludedPaths;
- // destroy old contexts
- for (var contextInfo in contextInfos) {
- var isIncluded = includedFolders.any((folder) {
- return folder.isOrContains(contextInfo.folder.path);
- });
- if (!isIncluded) {
- _destroyContext(contextInfo);
- }
- }
- // create new contexts
- for (var includedFolder in includedFolders) {
- var includedPath = includedFolder.path;
- var isManaged = rootInfo._managesOrHasChildThatManages(includedPath);
- if (!isManaged) {
- var parent = _getParentForNewContext(includedPath);
- changeSubscriptions[includedFolder] = includedFolder.changes
- .listen(_handleWatchEvent, onError: _handleWatchInterruption);
- _createContexts(parent, includedFolder, excludedPaths, false);
- }
- }
- // remove newly excluded sources
- for (var info in rootInfo.descendants) {
- // prepare excluded sources
- Map<String, Source> excludedSources = HashMap<String, Source>();
- info.sources.forEach((String path, Source source) {
- if (_isExcludedBy(excludedPaths, path) &&
- !_isExcludedBy(oldExcludedPaths, path)) {
- excludedSources[path] = source;
- }
- });
- // apply exclusion
- var changeSet = ChangeSet();
- excludedSources.forEach((String path, Source source) {
- info.sources.remove(path);
- changeSet.removedSource(path);
- });
- callbacks.applyChangesToContext(info.folder, changeSet);
- }
- // add previously excluded sources
- for (var info in rootInfo.descendants) {
- var changeSet = ChangeSet();
- _addPreviouslyExcludedSources(
- info, changeSet, info.folder, oldExcludedPaths);
- callbacks.applyChangesToContext(info.folder, changeSet);
- }
- }
- /// Recursively adds all Dart and HTML files to the [changeSet].
- void _addPreviouslyExcludedSources(ContextInfo info, ChangeSet changeSet,
- Folder folder, List<String> oldExcludedPaths) {
- if (info.excludesResource(folder)) {
- return;
- }
- List<Resource> children;
- try {
- children = folder.getChildren();
- } on FileSystemException {
- // The folder no longer exists, or cannot be read, to there's nothing to
- // do.
- return;
- }
- for (var child in children) {
- var path = child.path;
- // Path is being ignored.
- if (info.ignored(path)) {
- continue;
- }
- // add files, recurse into folders
- if (child is File) {
- // ignore if should not be analyzed at all
- if (!_shouldFileBeAnalyzed(child)) {
- continue;
- }
- // ignore if was not excluded
- var wasExcluded = _isExcludedBy(oldExcludedPaths, path) &&
- !_isExcludedBy(excludedPaths, path);
- if (!wasExcluded) {
- continue;
- }
- // do add the file
- var source = createSourceInContext(info.analysisDriver, child);
- changeSet.addedSource(child.path);
- info.sources[path] = source;
- } else if (child is Folder) {
- _addPreviouslyExcludedSources(info, changeSet, child, oldExcludedPaths);
- }
- }
- }
-
- /// Recursively adds all Dart and HTML files to the [changeSet].
- void _addSourceFiles(ChangeSet changeSet, Folder folder, ContextInfo info) {
- if (info.excludesResource(folder) ||
- folder.shortName.startsWith('.') ||
- _isInTopLevelDocDir(info.folder.path, folder.path)) {
- return;
- }
- List<Resource> children;
- try {
- children = folder.getChildren();
- } on FileSystemException {
- // The directory either doesn't exist or cannot be read. Either way, there
- // are no children that need to be added.
- return;
- }
- for (var child in children) {
- var path = child.path;
- // ignore excluded files or folders
- if (_isExcluded(path) || info.excludes(path) || info.ignored(path)) {
- continue;
- }
- // add files, recurse into folders
- if (child is File) {
- if (_shouldFileBeAnalyzed(child)) {
- var source = createSourceInContext(info.analysisDriver, child);
- changeSet.addedSource(child.path);
- info.sources[path] = source;
- }
- } else if (child is Folder) {
- _addSourceFiles(changeSet, child, info);
- }
- }
+ _createAnalysisContexts();
}
/// Use the given analysis [driver] to analyze the content of the analysis
@@ -839,387 +408,120 @@
convertedErrors ?? <protocol.AnalysisError>[]);
}
- void _checkForAnalysisOptionsUpdate(String path, ContextInfo info) {
- if (AnalysisEngine.isAnalysisOptionsFileName(path, pathContext)) {
- var driver = info.analysisDriver;
- if (driver == null) {
- // I suspect that this happens as a result of a race condition: server
- // has determined that the file (at [path]) is in a context, but hasn't
- // yet created a driver for that context.
- return;
- }
- // TODO(brianwilkerson) Set exclusion patterns.
- _analyzeAnalysisOptionsFile(driver, path);
- _updateAnalysisOptions(info);
- }
- }
-
- void _checkForDataFileUpdate(String path, ContextInfo info) {
+ void _checkForDataFileUpdate(String path) {
if (_isDataFile(path)) {
- var driver = info.analysisDriver;
- if (driver == null) {
- // I suspect that this happens as a result of a race condition: server
- // has determined that the file (at [path]) is in a context, but hasn't
- // yet created a driver for that context.
- return;
- }
+ var context = getContextFor(path);
+ var driver = context.driver;
_analyzeDataFile(driver, path);
}
}
- void _checkForManifestUpdate(String path, ContextInfo info) {
+ void _checkForManifestUpdate(String path) {
if (_isManifest(path)) {
- var driver = info.analysisDriver;
- if (driver == null) {
- // I suspect that this happens as a result of a race condition: server
- // has determined that the file (at [path]) is in a context, but hasn't
- // yet created a driver for that context.
- return;
- }
+ var context = getContextFor(path);
+ var driver = context.driver;
_analyzeManifestFile(driver, path);
}
}
- void _checkForPackagespecUpdate(String path, ContextInfo info) {
- // Check to see if this is `.dart_tool/package_config.json` or `.packages`
- // file for this context and if so, update the context's source factory.
- if (_isPackageConfigJsonFilePath(path) || _isDotPackagesFilePath(path)) {
- var driver = info.analysisDriver;
- if (driver == null) {
- // I suspect that this happens as a result of a race condition: server
- // has determined that the file (at [path]) is in a context, but hasn't
- // yet created a driver for that context.
- return;
- }
-
- _updateAnalysisOptions(info);
- final optionsFile = info?.analysisDriver?.contextRoot?.optionsFilePath;
- if (optionsFile != null) {
- _analyzeAnalysisOptionsFile(driver, optionsFile);
- }
- }
- }
-
- void _checkForPubspecUpdate(String path, ContextInfo info) {
- if (_isPubspec(path)) {
- var driver = info.analysisDriver;
- if (driver == null) {
- // I suspect that this happens as a result of a race condition: server
- // has determined that the file (at [path]) is in a context, but hasn't
- // yet created a driver for that context.
- return;
- }
- _analyzePubspecFile(driver, path);
- _updateAnalysisOptions(info);
- }
- }
-
- /// Compute the set of files that are being flushed, this is defined as
- /// the set of sources in the removed context (context.sources), that are
- /// orphaned by this context being removed (no other context includes this
- /// file.)
- List<String> _computeFlushedFiles(ContextInfo info) {
- var flushedFiles = info.analysisDriver.addedFiles.toSet();
- for (var contextInfo in rootInfo.descendants) {
- var other = contextInfo.analysisDriver;
- if (other != info.analysisDriver) {
- flushedFiles.removeAll(other.addedFiles);
- }
- }
- return flushedFiles.toList(growable: false);
- }
-
- /// Compute the appropriate [FolderDisposition] for [folder]. Use
- /// [addDependency] to indicate which files needed to be consulted in order to
- /// figure out the [FolderDisposition]; these dependencies will be watched in
- /// order to determine when it is necessary to call this function again.
- ///
- /// TODO(paulberry): use [addDependency] for tracking all folder disposition
- /// dependencies (currently we only use it to track "pub list" dependencies).
- FolderDisposition _computeFolderDisposition(Folder folder,
- void Function(String path) addDependency, File packagespecFile) {
- // Try .packages first.
- if (pathContext.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
- var packages = parsePackagesFile(
- resourceProvider,
- packagespecFile,
- );
- return PackagesFileDisposition(packages);
- }
-
- return NoPackageFolderDisposition();
- }
-
/// Compute line information for the given [content].
LineInfo _computeLineInfo(String content) {
var lineStarts = StringUtilities.computeLineStarts(content);
return LineInfo(lineStarts);
}
- /// Create an object that can be used to find and read the analysis options
- /// file for code being analyzed using the given [packages].
- AnalysisOptionsProvider _createAnalysisOptionsProvider(Packages packages) {
- var packageMap = <String, List<Folder>>{};
- if (packages != null) {
- for (var package in packages.packages) {
- packageMap[package.name] = [package.libFolder];
+ void _createAnalysisContexts() {
+ if (_collection != null) {
+ for (var analysisContext in _collection.contexts) {
+ var contextImpl = analysisContext as DriverBasedAnalysisContext;
+ _destroyContext(contextImpl);
}
}
- var resolvers = <UriResolver>[
- ResourceUriResolver(resourceProvider),
- PackageMapUriResolver(resourceProvider, packageMap),
- ];
- var sourceFactory = SourceFactory(resolvers);
- return AnalysisOptionsProvider(sourceFactory);
- }
-
- /// Create a new empty context associated with [folder], having parent
- /// [parent] and using [packagesFile] to resolve package URI's.
- ContextInfo _createContext(ContextInfo parent, Folder folder,
- List<String> excludedPaths, File packagesFile) {
- var dependencies = <String>[];
- var disposition =
- _computeFolderDisposition(folder, dependencies.add, packagesFile);
- var info = ContextInfo(this, parent, folder, packagesFile, disposition);
-
- File optionsFile;
- YamlMap optionMap;
- try {
- var provider = _createAnalysisOptionsProvider(disposition.packages);
- optionsFile = provider.getOptionsFile(info.folder);
- if (optionsFile != null) {
- optionMap = provider.getOptionsFromFile(optionsFile);
- }
- } catch (_) {
- // Parse errors are reported elsewhere.
- }
- var options = AnalysisOptionsImpl();
- applyToAnalysisOptions(options, optionMap);
-
- info.setDependencies(dependencies);
- var includedPath = folder.path;
- var containedExcludedPaths = excludedPaths
- .where((String excludedPath) =>
- pathContext.isWithin(includedPath, excludedPath))
- .toList();
- processOptionsForDriver(info, options, optionMap);
- var contextRoot = ContextRoot(folder.path, containedExcludedPaths,
- pathContext: pathContext);
- if (optionsFile != null) {
- contextRoot.optionsFilePath = optionsFile.path;
- }
- info.analysisDriver = callbacks.addAnalysisDriver(folder, contextRoot);
- _watchBazelFilesIfNeeded(folder, info.analysisDriver);
- if (optionsFile != null) {
- _analyzeAnalysisOptionsFile(info.analysisDriver, optionsFile.path);
- }
- var dataFile =
- folder.getChildAssumingFolder('lib').getChildAssumingFile(dataFileName);
- if (dataFile.exists) {
- _analyzeDataFile(info.analysisDriver, dataFile.path);
- }
- var pubspecFile = folder.getChildAssumingFile(PUBSPEC_NAME);
- if (pubspecFile.exists) {
- _analyzePubspecFile(info.analysisDriver, pubspecFile.path);
- }
-
- void checkManifestFilesIn(Folder folder) {
- // Don't traverse into dot directories.
- if (folder.shortName.startsWith('.')) {
- return;
- }
-
- for (var child in folder.getChildren()) {
- if (child is File) {
- if (child.shortName == MANIFEST_NAME &&
- !excludedPaths.contains(child.path)) {
- _analyzeManifestFile(info.analysisDriver, child.path);
- }
- } else if (child is Folder) {
- if (!excludedPaths.contains(child.path)) {
- checkManifestFilesIn(child);
- }
- }
- }
- }
-
- checkManifestFilesIn(folder);
- return info;
- }
-
- /// Potentially create a new context associated with the given [folder].
- ///
- /// If there are subfolders with 'pubspec.yaml' files, separate contexts are
- /// created for them and excluded from the context associated with the
- /// [folder].
- ///
- /// If [withPackageSpecOnly] is `true`, a context will be created only if
- /// there is a 'pubspec.yaml' or '.packages' file in the [folder].
- ///
- /// [parent] should be the parent of any contexts that are created.
- void _createContexts(ContextInfo parent, Folder folder,
- List<String> excludedPaths, bool withPackageSpecOnly) {
- if (_isExcluded(folder.path) || folder.shortName.startsWith('.')) {
- return;
- }
- // Decide whether a context needs to be created for [folder] here, and if
- // so, create it.
- var packageSpec = _findPackageSpecFile(folder);
- var createContext = packageSpec.exists || !withPackageSpecOnly;
- if (withPackageSpecOnly &&
- packageSpec.exists &&
- parent != null &&
- parent.ignored(packageSpec.path)) {
- // Don't create a context if the package spec is required and ignored.
- createContext = false;
- }
- if (createContext) {
- parent = _createContext(parent, folder, excludedPaths, packageSpec);
- }
-
- // Try to find subfolders with pubspecs or .packages files.
- try {
- for (var child in folder.getChildren()) {
- if (child is Folder) {
- if (!parent.ignored(child.path)) {
- _createContexts(parent, child, excludedPaths, true);
- }
- }
- }
- } on FileSystemException {
- // The directory either doesn't exist or cannot be read. Either way, there
- // are no subfolders that need to be added.
- }
-
- if (createContext) {
- // Now that the child contexts have been created, add the sources that
- // don't belong to the children.
- var changeSet = ChangeSet();
- _addSourceFiles(changeSet, folder, parent);
- callbacks.applyChangesToContext(folder, changeSet);
- }
- }
-
- /// Set up a [SourceFactory] that resolves packages as appropriate for the
- /// given [folder].
- SourceFactory _createSourceFactory(Folder folder) {
- var builder = callbacks.createContextBuilder(folder);
- var workspace = ContextBuilder.createWorkspace(
+ _collection = AnalysisContextCollectionImpl(
+ includedPaths: includedPaths,
+ excludedPaths: excludedPaths,
+ byteStore: _byteStore,
+ drainStreams: false,
+ enableIndex: true,
+ performanceLog: _performanceLog,
resourceProvider: resourceProvider,
- options: builder.builderOptions,
- rootPath: folder.path,
- lookForBazelBuildFileSubstitutes: false,
+ scheduler: _scheduler,
+ sdkPath: sdkManager.defaultSdkDirectory,
);
- return builder.createSourceFactory(folder.path, workspace);
+
+ for (var context in _collection.contexts) {
+ var contextImpl = context as DriverBasedAnalysisContext;
+ var driver = contextImpl.driver;
+
+ callbacks.listenAnalysisDriver(driver);
+
+ var rootFolder = contextImpl.contextRoot.root;
+ driverMap[rootFolder] = driver;
+
+ changeSubscriptions[rootFolder] = rootFolder.changes
+ .listen(_handleWatchEvent, onError: _handleWatchInterruption);
+
+ _watchBazelFilesIfNeeded(rootFolder, driver);
+
+ for (var file in contextImpl.contextRoot.analyzedFiles()) {
+ if (_isContainedInDotFolder(contextImpl.contextRoot.root.path, file)) {
+ continue;
+ }
+ driver.addFile(file);
+ }
+
+ var optionsFile = context.contextRoot.optionsFile;
+ if (optionsFile != null) {
+ _analyzeAnalysisOptionsFile(driver, optionsFile.path);
+ }
+
+ var dataFile = rootFolder
+ .getChildAssumingFolder('lib')
+ .getChildAssumingFile(dataFileName);
+ if (dataFile.exists) {
+ _analyzeDataFile(driver, dataFile.path);
+ }
+
+ var pubspecFile = rootFolder.getChildAssumingFile(PUBSPEC_NAME);
+ if (pubspecFile.exists) {
+ _analyzePubspecFile(driver, pubspecFile.path);
+ }
+
+ void checkManifestFilesIn(Folder folder) {
+ // Don't traverse into dot directories.
+ if (folder.shortName.startsWith('.')) {
+ return;
+ }
+
+ for (var child in folder.getChildren()) {
+ if (child is File) {
+ if (child.shortName == MANIFEST_NAME &&
+ !excludedPaths.contains(child.path)) {
+ _analyzeManifestFile(driver, child.path);
+ }
+ } else if (child is Folder) {
+ if (!excludedPaths.contains(child.path)) {
+ checkManifestFilesIn(child);
+ }
+ }
+ }
+ }
+
+ checkManifestFilesIn(rootFolder);
+ }
+
+ callbacks.afterContextsCreated();
}
/// Clean up and destroy the context associated with the given folder.
- void _destroyContext(ContextInfo info) {
- changeSubscriptions.remove(info.folder)?.cancel();
- bazelSubscriptions.remove(info.folder)?.cancel();
- callbacks.removeContext(info.folder, _computeFlushedFiles(info));
- var wasRemoved = info.parent.children.remove(info);
- assert(wasRemoved);
- }
+ void _destroyContext(DriverBasedAnalysisContext context) {
+ var rootFolder = context.contextRoot.root;
+ changeSubscriptions.remove(rootFolder)?.cancel();
+ bazelSubscriptions.remove(rootFolder)?.cancel();
- /// Extract a new [packagespecFile]-based context from [oldInfo].
- void _extractContext(ContextInfo oldInfo, File packagespecFile) {
- var newFolder = packagespecFile.parent2;
- var newInfo =
- _createContext(oldInfo, newFolder, excludedPaths, packagespecFile);
- // prepare sources to extract
- Map<String, Source> extractedSources = HashMap<String, Source>();
- oldInfo.sources.forEach((path, source) {
- if (newFolder.contains(path)) {
- extractedSources[path] = source;
- }
- });
- // update new context
- {
- var changeSet = ChangeSet();
- extractedSources.forEach((path, source) {
- newInfo.sources[path] = source;
- changeSet.addedSource(path);
- });
- callbacks.applyChangesToContext(newFolder, changeSet);
- }
- // update old context
- {
- var changeSet = ChangeSet();
- extractedSources.forEach((path, source) {
- oldInfo.sources.remove(path);
- changeSet.removedSource(path);
- });
- callbacks.applyChangesToContext(oldInfo.folder, changeSet);
- }
- // TODO(paulberry): every context that was previously a child of oldInfo is
- // is still a child of oldInfo. This is wrong--some of them ought to be
- // adopted by newInfo now.
- }
-
- /// Find the file that should be used to determine whether a context needs to
- /// be created here--this is either the ".packages" file or the "pubspec.yaml"
- /// file.
- File _findPackageSpecFile(Folder folder) {
- // Decide whether a context needs to be created for [folder] here, and if
- // so, create it.
- File packageSpec;
-
- // Start by looking for .packages.
- packageSpec = folder.getChild(PACKAGE_SPEC_NAME);
-
- // Fall back to looking for a pubspec.
- if (packageSpec == null || !packageSpec.exists) {
- packageSpec = folder.getChild(PUBSPEC_NAME);
- }
- return packageSpec;
- }
-
- /// Get analysis options inherited from an `_embedder.yaml` (deprecated)
- /// and/or a package specified configuration. If more than one
- /// `_embedder.yaml` is associated with the given context, the embedder is
- /// skipped.
- ///
- /// Returns null if there are no embedded/configured options.
- YamlMap _getEmbeddedOptions(ContextInfo info) {
- Map embeddedOptions;
- var locator = info.disposition.getEmbedderLocator(resourceProvider);
- var maps = locator.embedderYamls.values;
- if (maps.length == 1) {
- embeddedOptions = maps.first;
- }
- return embeddedOptions;
- }
-
- /// Return the [ContextInfo] for the "innermost" context whose associated
- /// folder is or contains the given path. ("innermost" refers to the nesting
- /// of contexts, so if there is a context for path /foo and a context for
- /// path /foo/bar, then the innermost context containing /foo/bar/baz.dart is
- /// the context for /foo/bar.)
- ///
- /// If no context contains the given path, `null` is returned.
- ContextInfo _getInnermostContextInfoFor(String path) {
- var info = rootInfo.findChildInfoFor(path);
- if (info == null) {
- return null;
- }
- while (true) {
- var childInfo = info.findChildInfoFor(path);
- if (childInfo == null) {
- return info;
- }
- info = childInfo;
- }
- }
-
- /// Return the parent for a new [ContextInfo] with the given [path] folder.
- ContextInfo _getParentForNewContext(String path) {
- var parent = _getInnermostContextInfoFor(path);
- if (parent != null) {
- return parent;
- }
- return rootInfo;
+ var flushedFiles = context.driver.addedFiles.toList();
+ callbacks.removeContext(rootFolder, flushedFiles);
}
/// Establishes watch(es) for the Bazel generated files provided in
@@ -1277,129 +579,48 @@
// that's the only context that's watching the file).
var path = event.path;
var type = event.type;
- var info = _getInnermostContextInfoFor(path);
- if (info == null) {
- // This event doesn't apply to any context. This could happen due to a
- // race condition (e.g. a context was removed while one of its events was
- // in the event loop). The event is inapplicable now, so just ignore it.
+
+ _instrumentationService.logWatchEvent('<unknown>', path, type.toString());
+
+ if (_isPackageConfigJsonFilePath(path) ||
+ _isDotPackagesFilePath(path) ||
+ _isPubspec(path) ||
+ AnalysisEngine.isAnalysisOptionsFileName(path, pathContext)) {
+ _createAnalysisContexts();
return;
}
- _instrumentationService.logWatchEvent(
- info.folder.path, path, type.toString());
- // First handle changes that affect folderDisposition (since these need to
- // be processed regardless of whether they are part of an excluded/ignored
- // path).
- if (info.hasDependency(path)) {
- _recomputeFolderDisposition(info);
- }
- _checkForPackagespecUpdate(path, info);
-
- // maybe excluded globally
- if (_isExcluded(path) ||
- _isContainedInDotFolder(info.folder.path, path,
- exclude: {'.dart_tool'}) ||
- _isInTopLevelDocDir(info.folder.path, path)) {
- return;
- }
- // maybe excluded from the context, so other context will handle it
- if (info.excludes(path)) {
- return;
- }
- // handle the change
- switch (type) {
- case ChangeType.ADD:
- var resource = resourceProvider.getResource(path);
-
- var directoryPath = pathContext.dirname(path);
-
- // Check to see if we need to create a new context.
- if (info.isTopLevel) {
- // Only create a new context if this is not the same directory
- // described by our info object.
- if (info.folder.path != directoryPath) {
- if (_isPubspec(path)) {
- // Check for a sibling .packages file.
- if (!resourceProvider
- .getFile(pathContext.join(directoryPath, PACKAGE_SPEC_NAME))
- .exists) {
- _extractContext(info, resource);
- return;
- }
+ if (path.endsWith('.dart')) {
+ for (var analysisContext_ in _collection.contexts) {
+ var analysisContext = analysisContext_ as DriverBasedAnalysisContext;
+ switch (type) {
+ case ChangeType.ADD:
+ // TODO(scheglov) Why not `isInAnalysisRoot()`?
+ if (_isContainedInDotFolder(
+ analysisContext.contextRoot.root.path, path)) {
+ return;
}
- if (_isDotPackagesFilePath(path)) {
- // Check for a sibling pubspec.yaml file.
- if (!resourceProvider
- .getFile(pathContext.join(directoryPath, PUBSPEC_NAME))
- .exists) {
- _extractContext(info, resource);
- return;
- }
- }
- }
- }
-
- // If the file went away and was replaced by a folder before we
- // had a chance to process the event, resource might be a Folder. In
- // that case don't add it.
- if (resource is File) {
- if (_shouldFileBeAnalyzed(resource)) {
- info.analysisDriver.addFile(path);
- }
- }
- break;
- case ChangeType.REMOVE:
- // If package spec info is removed, check to see if we can merge
- // contexts. Note that it's important to verify that there is NEITHER a
- // .packages nor a lingering pubspec.yaml before merging.
- if (!info.isTopLevel) {
- var directoryPath = pathContext.dirname(path);
-
- // Only merge if this is the same directory described by our info
- // object.
- if (info.folder.path == directoryPath) {
- if (_isPubspec(path)) {
- // Check for a sibling .packages file.
- if (!resourceProvider
- .getFile(pathContext.join(directoryPath, PACKAGE_SPEC_NAME))
- .exists) {
- _mergeContext(info);
- return;
- }
- }
- if (_isDotPackagesFilePath(path)) {
- // Check for a sibling pubspec.yaml file.
- if (!resourceProvider
- .getFile(pathContext.join(directoryPath, PUBSPEC_NAME))
- .exists) {
- _mergeContext(info);
- return;
- }
- }
- }
- }
-
- var resource = resourceProvider.getResource(path);
- if (resource is File &&
- _shouldFileBeAnalyzed(resource, mustExist: false)) {
- callbacks.applyFileRemoved(info.analysisDriver, path);
- }
- break;
- case ChangeType.MODIFY:
- var resource = resourceProvider.getResource(path);
- if (resource is File) {
- if (_shouldFileBeAnalyzed(resource)) {
- for (var driver in driverMap.values) {
- driver.changeFile(path);
+ analysisContext.driver.addFile(path);
+ break;
+ case ChangeType.MODIFY:
+ analysisContext.driver.changeFile(path);
+ break;
+ case ChangeType.REMOVE:
+ analysisContext.driver.removeFile(path);
+ // TODO(scheglov) Why not `isInAnalysisRoot()`?
+ // TODO(scheglov) Why not always?
+ var resource = resourceProvider.getResource(path);
+ if (resource is File &&
+ _shouldFileBeAnalyzed(resource, mustExist: false)) {
+ callbacks.applyFileRemoved(path);
}
break;
- }
}
+ }
}
- _checkForAnalysisOptionsUpdate(path, info);
- _checkForDataFileUpdate(path, info);
- _checkForPubspecUpdate(path, info);
- _checkForManifestUpdate(path, info);
+
+ _checkForManifestUpdate(path);
+ _checkForDataFileUpdate(path);
}
/// On windows, the directory watcher may overflow, and we must recover.
@@ -1408,7 +629,7 @@
AnalysisEngine.instance.instrumentationService
.logError('Watcher error; refreshing contexts.\n$error\n$stackTrace');
// TODO(mfairhurst): Optimize this, or perhaps be less complete.
- refresh(null);
+ refresh();
}
/// Determine whether the given [path], when interpreted relative to the
@@ -1438,31 +659,6 @@
return pathContext.basename(path) == PACKAGE_SPEC_NAME;
}
- /// Returns `true` if the given [path] is excluded by [excludedPaths].
- bool _isExcluded(String path) => _isExcludedBy(excludedPaths, path);
-
- /// Returns `true` if the given [path] is excluded by [excludedPaths].
- bool _isExcludedBy(List<String> excludedPaths, String path) {
- return excludedPaths.any((excludedPath) {
- if (pathContext.isWithin(excludedPath, path)) {
- return true;
- }
- return path == excludedPath;
- });
- }
-
- /// Determine whether the given [path] is in the direct 'doc' folder of the
- /// context root [root].
- bool _isInTopLevelDocDir(String root, String path) {
- var rootPrefix = root + pathContext.separator;
- if (path.startsWith(rootPrefix)) {
- var suffix = path.substring(rootPrefix.length);
- return suffix == DOC_DIR_NAME ||
- suffix.startsWith(DOC_DIR_NAME + pathContext.separator);
- }
- return false;
- }
-
bool _isManifest(String path) => pathContext.basename(path) == MANIFEST_NAME;
bool _isPackageConfigJsonFilePath(String path) {
@@ -1474,41 +670,12 @@
bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME;
- /// Merges [info] context into its parent.
- void _mergeContext(ContextInfo info) {
- // destroy the context
- _destroyContext(info);
- // add files to the parent context
- var parentInfo = info.parent;
- if (parentInfo != null) {
- parentInfo.children.remove(info);
- var changeSet = ChangeSet();
- info.sources.forEach((path, source) {
- parentInfo.sources[path] = source;
- changeSet.addedSource(path);
- });
- callbacks.applyChangesToContext(parentInfo.folder, changeSet);
- }
- }
-
/// Read the contents of the file at the given [path], or throw an exception
/// if the contents cannot be read.
String _readFile(String path) {
return resourceProvider.getFile(path).readAsStringSync();
}
- /// Recompute the [FolderDisposition] for the context described by [info],
- /// and update the client appropriately.
- void _recomputeFolderDisposition(ContextInfo info) {
- // TODO(paulberry): when computePackageMap is changed into an
- // asynchronous API call, we'll want to suspend analysis for this context
- // while we're rerunning "pub list", since any analysis we complete while
- // "pub list" is in progress is just going to get thrown away anyhow.
- var dependencies = <String>[];
- info.setDependencies(dependencies);
- _updateContextPackageUriResolver(info.folder);
- }
-
/// Return `true` if the given [file] should be analyzed.
bool _shouldFileBeAnalyzed(File file, {bool mustExist = true}) {
for (var glob in analyzedFilesGlobs) {
@@ -1525,39 +692,6 @@
return false;
}
- void _updateAnalysisOptions(ContextInfo info) {
- var driver = info.analysisDriver;
- var contextRoot = info.folder.path;
- var builder = callbacks.createContextBuilder(info.folder);
- var workspace = ContextBuilder.createWorkspace(
- resourceProvider: resourceProvider,
- options: builder.builderOptions,
- rootPath: contextRoot,
- lookForBazelBuildFileSubstitutes: false,
- );
- var options = builder.getAnalysisOptions(contextRoot, workspace,
- contextRoot: driver.contextRoot);
- var packages = ContextBuilder.createPackageMap(
- resourceProvider: builder.resourceProvider,
- options: builder.builderOptions,
- rootPath: contextRoot,
- );
- var factory = builder.createSourceFactory(contextRoot, workspace);
- driver.configure(
- analysisOptions: options,
- packages: packages,
- sourceFactory: factory,
- );
- callbacks.analysisOptionsUpdated(driver);
- }
-
- void _updateContextPackageUriResolver(Folder contextFolder) {
- var info = getContextInfoFor(contextFolder);
- var driver = info.analysisDriver;
- var sourceFactory = _createSourceFactory(contextFolder);
- driver.configure(sourceFactory: sourceFactory);
- }
-
/// Listens to files generated by Bazel that were found or searched for.
///
/// This is handled specially because the files are outside the package
@@ -1589,85 +723,6 @@
}
}
-/// An instance of the class [FolderDisposition] represents the information
-/// gathered by the [ContextManagerImpl] to determine how to create an analysis
-/// driver for a given folder.
-///
-/// Note: [ContextManagerImpl] may use equality testing and hash codes to
-/// determine when two folders should share the same context, so derived classes
-/// may need to override operator== and hashCode() if object identity is
-/// insufficient.
-///
-/// TODO(paulberry): consider adding a flag to indicate that it is not necessary
-/// to recurse into the given folder looking for additional contexts to create
-/// or files to analyze (this could help avoid unnecessarily weighing down the
-/// system with file watchers).
-abstract class FolderDisposition {
- /// If this [FolderDisposition] was created based on a package root
- /// folder, the absolute path to that folder. Otherwise `null`.
- String get packageRoot;
-
- /// If contexts governed by this [FolderDisposition] should resolve packages
- /// using the ".packages" file mechanism (DEP 5), retrieve the [Packages]
- /// object that resulted from parsing the ".packages" file.
- Packages get packages;
-
- /// Return the locator used to locate the _embedder.yaml file used to
- /// configure the SDK. The [resourceProvider] is used to access the file
- /// system in cases where that is necessary.
- EmbedderYamlLocator getEmbedderLocator(ResourceProvider resourceProvider);
-}
-
-/// Concrete [FolderDisposition] object indicating that the context for a given
-/// folder should not resolve "package:" URIs at all.
-class NoPackageFolderDisposition extends FolderDisposition {
- @override
- final String packageRoot;
-
- NoPackageFolderDisposition({this.packageRoot});
-
- @override
- Packages get packages => null;
-
- @override
- EmbedderYamlLocator getEmbedderLocator(ResourceProvider resourceProvider) =>
- EmbedderYamlLocator(null);
-}
-
-/// Concrete [FolderDisposition] object indicating that the context for a given
-/// folder should resolve packages using a ".packages" file.
-class PackagesFileDisposition extends FolderDisposition {
- @override
- final Packages packages;
-
- Map<String, List<Folder>> packageMap;
-
- EmbedderYamlLocator _embedderLocator;
-
- PackagesFileDisposition(this.packages);
-
- @override
- String get packageRoot => null;
-
- Map<String, List<Folder>> buildPackageMap(ResourceProvider resourceProvider) {
- if (packageMap == null) {
- packageMap = <String, List<Folder>>{};
- if (packages != null) {
- for (var package in packages.packages) {
- packageMap[package.name] = [package.libFolder];
- }
- }
- }
- return packageMap;
- }
-
- @override
- EmbedderYamlLocator getEmbedderLocator(ResourceProvider resourceProvider) {
- _embedderLocator ??= EmbedderYamlLocator(buildPackageMap(resourceProvider));
- return _embedderLocator;
- }
-}
-
/// A watcher with subscription used to detect changes to some file.
class _BazelFilesSubscription {
final BazelFileWatcher watcher;
diff --git a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
index fa3c671..574c964 100644
--- a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
@@ -78,16 +78,20 @@
// used to generate errors that can then be fixed.
// TODO(danrubel): Rework to use a different approach if this command
// will be used from within the IDE.
- contextManager.refresh(null);
+ contextManager.refresh();
for (var filePath in params.included) {
if (!server.isValidFilePath(filePath)) {
return Response.invalidFilePathFormat(request, filePath);
}
+
+ var analysisContext = contextManager.getContextFor(filePath);
+ if (analysisContext == null) {
+ return Response.fileNotAnalyzed(request, filePath);
+ }
+
var res = resourceProvider.getResource(filePath);
- if (!res.exists ||
- !(contextManager.includedPaths.contains(filePath) ||
- contextManager.isInAnalysisRoot(filePath))) {
+ if (!res.exists) {
return Response.fileNotAnalyzed(request, filePath);
}
@@ -95,16 +99,16 @@
// within an IDE, then this will cause the lint results to change.
// TODO(danrubel): Rework to use a different approach if this command
// will be used from within the IDE.
- var driver = contextManager.getDriverFor(filePath);
+ var driver = analysisContext.driver;
var analysisOptions = driver.analysisOptions as AnalysisOptionsImpl;
analysisOptions.lint = true;
analysisOptions.lintRules = linters;
- var contextFolder = contextManager.getContextFolderFor(filePath);
- var pkgFolder = findPkgFolder(contextFolder);
- if (pkgFolder != null && !pkgFolders.contains(pkgFolder)) {
+ var pkgFolder = analysisContext.contextRoot.root;
+ if (!pkgFolders.contains(pkgFolder)) {
pkgFolders.add(pkgFolder);
}
+
if (res is Folder) {
fixFolders.add(res);
} else {
@@ -171,8 +175,7 @@
if (res is Folder) {
for (var child in res.getChildren()) {
if (!child.shortName.startsWith('.') &&
- contextManager.isInAnalysisRoot(child.path) &&
- !contextManager.isIgnored(child.path)) {
+ contextManager.isInAnalysisRoot(child.path)) {
resources.add(child);
}
}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index fe63631..6cbe8fa 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -45,7 +45,6 @@
import 'package:analyzer/src/dart/analysis/results.dart' as engine;
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart' as engine;
-import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/exception/exception.dart';
import 'package:analyzer/src/generated/engine.dart' as engine;
import 'package:analyzer/src/generated/engine.dart';
@@ -108,15 +107,10 @@
var processor = BulkFixProcessor(server.instrumentationService, workspace,
useConfigFiles: params.inTestMode);
- String sdkPath;
- var sdk = server.findSdk();
- if (sdk is FolderBasedDartSdk) {
- sdkPath = sdk.directory.path;
- }
var collection = AnalysisContextCollectionImpl(
includedPaths: params.included,
resourceProvider: server.resourceProvider,
- sdkPath: sdkPath,
+ sdkPath: server.sdkPath,
);
var changeBuilder = await processor.fixErrors(collection.contexts);
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index cc0212f..993388c 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -39,8 +39,6 @@
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/source/line_info.dart';
-import 'package:analyzer/src/context/builder.dart';
-import 'package:analyzer/src/context/context_root.dart';
import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
import 'package:analyzer/src/dart/analysis/status.dart' as nd;
import 'package:analyzer/src/generated/engine.dart';
@@ -345,8 +343,7 @@
bool isAnalyzedFile(String file) {
return contextManager.isInAnalysisRoot(file) &&
// Dot folders are not analyzed (skipped over in _handleWatchEventImpl)
- !contextManager.isContainedInDotFolder(file) &&
- !contextManager.isIgnored(file);
+ !contextManager.isContainedInDotFolder(file);
}
/// Logs the error on the client using window/logMessage.
@@ -705,11 +702,9 @@
_lastIncludedRootPaths = includedPaths;
_lastExcludedRootPaths = excludedPaths;
- declarationsTracker?.discardContexts();
notificationManager.setAnalysisRoots(
includedPaths.toList(), excludedPaths.toList());
contextManager.setRoots(includedPaths.toList(), excludedPaths.toList());
- addContextsToDeclarationsTracker();
}
void _updateDriversAndPluginsPriorityFiles() {
@@ -780,14 +775,32 @@
analysisServer.notificationManager;
@override
- nd.AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot) {
- var builder = createContextBuilder(folder);
- var workspace = ContextBuilder.createWorkspace(
- resourceProvider: resourceProvider,
- options: builder.builderOptions,
- rootPath: folder.path,
- );
- var analysisDriver = builder.buildDriver(contextRoot, workspace);
+ void afterContextsCreated() {
+ analysisServer.addContextsToDeclarationsTracker();
+ }
+
+ @override
+ void afterWatchEvent(WatchEvent event) {
+ // TODO: implement afterWatchEvent
+ }
+
+ @override
+ void applyFileRemoved(String file) {
+ analysisServer.publishDiagnostics(file, []);
+ }
+
+ @override
+ void broadcastWatchEvent(WatchEvent event) {
+ analysisServer.notifyDeclarationsTracker(event.path);
+ analysisServer.notifyFlutterWidgetDescriptions(event.path);
+ analysisServer.pluginManager.broadcastWatchEvent(event);
+ }
+
+ @override
+ void listenAnalysisDriver(nd.AnalysisDriver analysisDriver) {
+ analysisServer.declarationsTracker
+ ?.addContext(analysisDriver.analysisContext);
+
final textDocumentCapabilities =
analysisServer.clientCapabilities?.textDocument;
final supportedDiagnosticTags = HashSet<DiagnosticTag>.of(
@@ -835,66 +848,6 @@
});
analysisDriver.exceptions.listen(analysisServer.logExceptionResult);
analysisDriver.priorityFiles = analysisServer.priorityFiles.toList();
- analysisServer.driverMap[folder] = analysisDriver;
- return analysisDriver;
- }
-
- @override
- void afterContextRefresh() {
- analysisServer.addContextsToDeclarationsTracker();
- }
-
- @override
- void afterWatchEvent(WatchEvent event) {
- // TODO: implement afterWatchEvent
- }
-
- @override
- void analysisOptionsUpdated(nd.AnalysisDriver driver) {
- // TODO: implement analysisOptionsUpdated
- }
-
- @override
- void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
- var analysisDriver = analysisServer.driverMap[contextFolder];
- if (analysisDriver != null) {
- changeSet.addedFiles.forEach((path) {
- analysisDriver.addFile(path);
- });
- changeSet.changedFiles.forEach((path) {
- analysisDriver.changeFile(path);
- });
- changeSet.removedFiles.forEach((path) {
- analysisDriver.removeFile(path);
- });
- }
- }
-
- @override
- void applyFileRemoved(nd.AnalysisDriver driver, String file) {
- driver.removeFile(file);
- analysisServer.publishDiagnostics(file, []);
- }
-
- @override
- void broadcastWatchEvent(WatchEvent event) {
- analysisServer.notifyDeclarationsTracker(event.path);
- analysisServer.notifyFlutterWidgetDescriptions(event.path);
- analysisServer.pluginManager.broadcastWatchEvent(event);
- }
-
- @override
- ContextBuilder createContextBuilder(Folder folder) {
- var builderOptions = ContextBuilderOptions();
- var builder = ContextBuilder(
- resourceProvider, analysisServer.sdkManager, null,
- options: builderOptions);
- builder.analysisDriverScheduler = analysisServer.analysisDriverScheduler;
- builder.performanceLog = analysisServer.analysisPerformanceLogger;
- builder.byteStore = analysisServer.byteStore;
- builder.enableIndex = true;
- builder.lookForBazelBuildFileSubstitutes = false;
- return builder;
}
@override
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
index df8ec96..7ed705f 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
@@ -94,13 +94,6 @@
}
@override
- void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
- if (opType.includeTypeNameSuggestions) {
- builder.suggestTypeAlias(element, prefix: prefix);
- }
- }
-
- @override
void visitLibraryElement(LibraryElement element) {
if (visitedLibraries.add(element)) {
element.visitChildren(this);
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index 5614cd3..9438324 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -128,10 +128,7 @@
/// Creates a project [projectPath].
void createProject({Map<String, String> packageRoots}) {
newFolder(projectPath);
- var request = AnalysisSetAnalysisRootsParams([projectPath], [],
- packageRoots: packageRoots)
- .toRequest('0');
- handleSuccessfulRequest(request, handler: analysisHandler);
+ setRoots(included: [projectPath], excluded: []);
}
void doAllDeclarationsTrackerWork() {
@@ -190,6 +187,18 @@
handleSuccessfulRequest(request);
}
+ void setRoots({
+ @required List<String> included,
+ @required List<String> excluded,
+ }) {
+ var includedConverted = included.map(convertPath).toList();
+ var excludedConverted = excluded.map(convertPath).toList();
+ var request = AnalysisSetAnalysisRootsParams(
+ includedConverted, excludedConverted,
+ packageRoots: {}).toRequest('0');
+ handleSuccessfulRequest(request, handler: analysisHandler);
+ }
+
@mustCallSuper
void setUp() {
serverChannel = MockServerChannel();
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
deleted file mode 100644
index 7acd0c6..0000000
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ /dev/null
@@ -1,2399 +0,0 @@
-// Copyright (c) 2014, 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/src/context_manager.dart';
-import 'package:analysis_server/src/plugin/notification_manager.dart';
-import 'package:analysis_server/src/utilities/null_string_sink.dart';
-import 'package:analyzer/error/error.dart';
-import 'package:analyzer/exception/exception.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/instrumentation/instrumentation.dart';
-import 'package:analyzer/source/error_processor.dart';
-import 'package:analyzer/src/context/builder.dart';
-import 'package:analyzer/src/context/context_root.dart';
-import 'package:analyzer/src/dart/analysis/byte_store.dart';
-import 'package:analyzer/src/dart/analysis/driver.dart';
-import 'package:analyzer/src/dart/analysis/file_state.dart';
-import 'package:analyzer/src/dart/analysis/performance_logger.dart';
-import 'package:analyzer/src/error/codes.dart';
-import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer/src/generated/sdk.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/generated/source_io.dart';
-import 'package:analyzer/src/services/lint.dart';
-import 'package:analyzer/src/test_utilities/mock_sdk.dart';
-import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
-import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
-import 'package:analyzer/src/util/glob.dart';
-import 'package:linter/src/rules.dart';
-import 'package:linter/src/rules/avoid_as.dart';
-import 'package:path/path.dart' as path;
-import 'package:test/test.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-import 'package:watcher/watcher.dart';
-
-import 'src/plugin/plugin_manager_test.dart';
-
-void main() {
- defineReflectiveSuite(() {
- defineReflectiveTests(AbstractContextManagerTest);
- defineReflectiveTests(ContextManagerWithOptionsTest);
- });
-}
-
-/// Wrapper around the test package's `fail` function.
-///
-/// Unlike the test package's `fail` function, this function is not annotated
-/// with @alwaysThrows, so we can call it at the top of a test method without
-/// causing the rest of the method to be flagged as dead code.
-void _fail(String message) {
- fail(message);
-}
-
-@reflectiveTest
-class AbstractContextManagerTest extends ContextManagerTest {
- void test_contextsInAnalysisRoot_nestedContext() {
- var subProjPath = join(projPath, 'subproj');
- var subProjFolder = newFolder(subProjPath);
- newFile(join(subProjPath, 'pubspec.yaml'), content: 'contents');
- var subProjFilePath = join(subProjPath, 'file.dart');
- newFile(subProjFilePath, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
- // Make sure that there really are contexts for both the main project and
- // the subproject.
- var projectFolder = getFolder(projPath);
- var projContextInfo = manager.getContextInfoFor(projectFolder);
- expect(projContextInfo, isNotNull);
- expect(projContextInfo.folder, projectFolder);
- var subProjContextInfo = manager.getContextInfoFor(subProjFolder);
- expect(subProjContextInfo, isNotNull);
- expect(subProjContextInfo.folder, subProjFolder);
- expect(projContextInfo.analysisDriver,
- isNot(equals(subProjContextInfo.analysisDriver)));
- // Check that getDriversInAnalysisRoot() works.
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, isNotNull);
- expect(drivers, hasLength(2));
- expect(drivers, contains(projContextInfo.analysisDriver));
- expect(drivers, contains(subProjContextInfo.analysisDriver));
- }
-
- @failingTest
- Future<void> test_embedder_added() async {
- // NoSuchMethodError: The getter 'apiSignature' was called on null.
- // Receiver: null
- // Tried calling: apiSignature
- // dart:core Object.noSuchMethod
- // package:analyzer/src/dart/analysis/driver.dart 460:20 AnalysisDriver.configure
- // package:analysis_server/src/context_manager.dart 1043:16 ContextManagerImpl._checkForPackagespecUpdate
- // package:analysis_server/src/context_manager.dart 1553:5 ContextManagerImpl._handleWatchEvent
- //return super.test_embedder_added();
- _fail('NoSuchMethodError');
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/nope.dart');
- var embedderPath = '$projPath/embedder';
- newFile('$embedderPath/entry.dart');
- var embedderSrcPath = '$projPath/embedder/src';
- newFile('$embedderSrcPath/part.dart');
-
- // Setup _embedder.yaml.
- newFile('$libPath/_embedder.yaml', content: r'''
-embedded_libs:
- "dart:foobar": "../embedder/entry.dart"
- "dart:typed_data": "../embedder/src/part"
- ''');
-
- var projectFolder = newFolder(projPath);
-
- // NOTE that this is Not in our package path yet.
-
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
- // Confirm that one driver / context was created.
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, isNotNull);
- expect(drivers, hasLength(1));
-
- // No embedded libs yet.
- expect(sourceFactory.forUri('dart:typed_data'), isNull);
-
- // Add .packages file that introduces a dependency with embedded libs.
- newFile('$projPath/.packages', content: r'''
-test_pack:lib/''');
-
- await pumpEventQueue();
-
- // Confirm that we still have just one driver / context.
- drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, isNotNull);
- expect(drivers, hasLength(1));
-
- // Embedded lib should be defined now.
- expect(sourceFactory.forUri('dart:typed_data'), isNotNull);
- }
-
- Future<void> test_embedder_packagespec() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/nope.dart');
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$sdkExtPath/entry.dart');
- var sdkExtSrcPath = '$projPath/sdk_ext/src';
- newFile('$sdkExtSrcPath/part.dart');
- // Setup _embedder.yaml.
- newFile('$libPath/_embedder.yaml', content: r'''
-embedded_libs:
- "dart:foobar": "../sdk_ext/entry.dart"
- "dart:typed_data": "../sdk_ext/src/part"
- ''');
- // Setup .packages file
- newFile('$projPath/.packages', content: r'''
-sky_engine:lib/''');
- // Setup context.
-
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
- // Confirm that one context was created.
- var count = manager.numberOfContextsInAnalysisRoot(newFolder(projPath));
- expect(count, equals(1));
- var source = sourceFactory.forUri('dart:foobar');
- expect(source, isNotNull);
- expect(source.fullName, convertPath('/my/proj/sdk_ext/entry.dart'));
- // We can't find dart:core because we didn't list it in our
- // embedded_libs map.
- expect(sourceFactory.forUri('dart:core'), isNull);
- // We can find dart:typed_data because we listed it in our
- // embedded_libs map.
- expect(sourceFactory.forUri('dart:typed_data'), isNotNull);
- }
-
- void test_isInAnalysisRoot_excluded() {
- // prepare paths
- var project = convertPath('/project');
- var excludedFolder = convertPath('$project/excluded');
- // set roots
- newFolder(project);
- newFolder(excludedFolder);
- manager.setRoots(<String>[project], <String>[excludedFolder]);
- // verify
- expect(manager.isInAnalysisRoot(convertPath('$excludedFolder/test.dart')),
- isFalse);
- }
-
- void test_isInAnalysisRoot_inNestedContext() {
- var subProjPath = join(projPath, 'subproj');
- var subProjFolder = newFolder(subProjPath);
- newFile(join(subProjPath, 'pubspec.yaml'), content: 'contents');
- var subProjFilePath = join(subProjPath, 'file.dart');
- newFile(subProjFilePath, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
- // Make sure that there really is a context for the subproject.
- var subProjContextInfo = manager.getContextInfoFor(subProjFolder);
- expect(subProjContextInfo, isNotNull);
- expect(subProjContextInfo.folder, subProjFolder);
- // Check that isInAnalysisRoot() works.
- expect(manager.isInAnalysisRoot(subProjFilePath), isTrue);
- }
-
- void test_isInAnalysisRoot_inRoot() {
- manager.setRoots(<String>[projPath], <String>[]);
- expect(manager.isInAnalysisRoot('$projPath/test.dart'), isTrue);
- }
-
- void test_isInAnalysisRoot_notInRoot() {
- manager.setRoots(<String>[projPath], <String>[]);
- expect(manager.isInAnalysisRoot('/test.dart'), isFalse);
- }
-
- Future<void> test_packagesFolder_areAnalyzed() {
- // create a context with a pubspec.yaml file
- var pubspecPath = join(projPath, 'pubspec.yaml');
- newFile(pubspecPath, content: 'pubspec');
- // create a file in the "packages" folder
- var filePath1 = join(projPath, 'packages', 'file1.dart');
- var file1 = newFile(filePath1, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
- expect(callbacks.currentFilePaths, unorderedEquals([file1.path]));
- var filePath2 = join(projPath, 'packages', 'file2.dart');
- var file2 = newFile(filePath2, content: 'contents');
- return pumpEventQueue().then((_) {
- expect(callbacks.currentFilePaths,
- unorderedEquals([file1.path, file2.path]));
- });
- }
-
- Future<void> test_refresh_folder_with_packagespec() {
- // create a context with a .packages file
- var packagespecFile = join(projPath, '.packages');
- newFile(packagespecFile, content: '');
- manager.setRoots(<String>[projPath], <String>[]);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- callbacks.now++;
- manager.refresh(null);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- expect(callbacks.currentContextTimestamps[projPath], callbacks.now);
- });
- });
- }
-
- // TODO(paulberry): This test only tests PackagesFileDisposition.
- // Once http://dartbug.com/23909 is fixed, add a test for sdk extensions
- // and PackageMapDisposition.
- Future<void> test_refresh_folder_with_packagespec_subfolders() {
- // Create a folder with no .packages file, containing two subfolders with
- // .packages files.
- var subdir1Path = join(projPath, 'subdir1');
- var subdir2Path = join(projPath, 'subdir2');
- var packagespec1Path = join(subdir1Path, '.packages');
- var packagespec2Path = join(subdir2Path, '.packages');
- newFile(packagespec1Path, content: '');
- newFile(packagespec2Path, content: '');
- manager.setRoots(<String>[projPath], <String>[]);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots,
- unorderedEquals([subdir1Path, subdir2Path, projPath]));
- callbacks.now++;
- manager.refresh(null);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots,
- unorderedEquals([subdir1Path, subdir2Path, projPath]));
- expect(callbacks.currentContextTimestamps[projPath], callbacks.now);
- expect(callbacks.currentContextTimestamps[subdir1Path], callbacks.now);
- expect(callbacks.currentContextTimestamps[subdir2Path], callbacks.now);
- });
- });
- }
-
- Future<void> test_refresh_folder_with_pubspec() {
- // create a context with a pubspec.yaml file
- var pubspecPath = join(projPath, 'pubspec.yaml');
- newFile(pubspecPath, content: 'pubspec');
- manager.setRoots(<String>[projPath], <String>[]);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- callbacks.now++;
- manager.refresh(null);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- expect(callbacks.currentContextTimestamps[projPath], callbacks.now);
- });
- });
- }
-
- Future<void> test_refresh_folder_with_pubspec_subfolders() {
- // Create a folder with no pubspec.yaml, containing two subfolders with
- // pubspec.yaml files.
- var subdir1Path = join(projPath, 'subdir1');
- var subdir2Path = join(projPath, 'subdir2');
- var pubspec1Path = join(subdir1Path, 'pubspec.yaml');
- var pubspec2Path = join(subdir2Path, 'pubspec.yaml');
- newFile(pubspec1Path, content: 'pubspec');
- newFile(pubspec2Path, content: 'pubspec');
- manager.setRoots(<String>[projPath], <String>[]);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots,
- unorderedEquals([subdir1Path, subdir2Path, projPath]));
- callbacks.now++;
- manager.refresh(null);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots,
- unorderedEquals([subdir1Path, subdir2Path, projPath]));
- expect(callbacks.currentContextTimestamps[projPath], callbacks.now);
- expect(callbacks.currentContextTimestamps[subdir1Path], callbacks.now);
- expect(callbacks.currentContextTimestamps[subdir2Path], callbacks.now);
- });
- });
- }
-
- Future<void> test_refresh_oneContext() {
- // create two contexts with pubspec.yaml files
- var pubspecPath = join(projPath, 'pubspec.yaml');
- newFile(pubspecPath, content: 'pubspec1');
-
- var proj2Path = convertPath('/my/proj2');
- newFolder(proj2Path);
- var pubspec2Path = join(proj2Path, 'pubspec.yaml');
- newFile(pubspec2Path, content: 'pubspec2');
-
- var roots = <String>[projPath, proj2Path];
- manager.setRoots(roots, <String>[]);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots, unorderedEquals(roots));
- var then = callbacks.now;
- callbacks.now++;
- manager.refresh([getFolder(proj2Path)]);
- return pumpEventQueue().then((_) {
- expect(callbacks.currentContextRoots, unorderedEquals(roots));
- expect(callbacks.currentContextTimestamps[projPath], then);
- expect(callbacks.currentContextTimestamps[proj2Path], callbacks.now);
- });
- });
- }
-
- void test_setRoots_addFolderWithDartFile() {
- var filePath = join(projPath, 'foo.dart');
- newFile(filePath, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- var drivers = manager.getDriversInAnalysisRoot(newFolder(projPath));
- expect(drivers, hasLength(1));
- expect(drivers[0], isNotNull);
- var result = sourceFactory.forUri('dart:async');
- expect(result, isNotNull);
- }
-
- void test_setRoots_addFolderWithDartFileInSubfolder() {
- var filePath = join(projPath, 'foo', 'bar.dart');
- newFile(filePath, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- }
-
- void test_setRoots_addFolderWithDummyLink() {
- var filePath = join(projPath, 'foo.dart');
- resourceProvider.newDummyLink(filePath);
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- expect(callbacks.currentFilePaths, isEmpty);
- }
-
- void test_setRoots_addFolderWithNestedPackageSpec() {
- var examplePath =
- convertPath('$projPath/${ContextManagerTest.EXAMPLE_NAME}');
- var libPath = convertPath('$projPath/${ContextManagerTest.LIB_NAME}');
-
- newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}');
- newFile('$libPath/main.dart');
- newFile('$examplePath/${ContextManagerImpl.PACKAGE_SPEC_NAME}');
- newFile('$examplePath/example.dart');
-
- manager.setRoots(<String>[projPath], <String>[]);
-
- expect(callbacks.currentContextRoots, hasLength(2));
-
- expect(callbacks.currentContextRoots, contains(projPath));
- var projSources = callbacks.currentFileSources(projPath);
- expect(projSources, hasLength(1));
- expect(projSources.first.uri, toUri('$libPath/main.dart'));
-
- expect(callbacks.currentContextRoots, contains(examplePath));
- var exampleSources = callbacks.currentFileSources(examplePath);
- expect(exampleSources, hasLength(1));
- expect(exampleSources.first.uri, toUri('$examplePath/example.dart'));
- }
-
- void test_setRoots_addFolderWithNestedPubspec() {
- var examplePath =
- convertPath('$projPath/${ContextManagerTest.EXAMPLE_NAME}');
- var libPath = convertPath('$projPath/${ContextManagerTest.LIB_NAME}');
-
- newFile('$projPath/${ContextManagerImpl.PUBSPEC_NAME}');
- newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'proj:lib/');
- newFile('$libPath/main.dart');
- newFile('$examplePath/${ContextManagerImpl.PUBSPEC_NAME}');
- newFile('$examplePath/example.dart');
-
- manager.setRoots(<String>[projPath], <String>[]);
-
- expect(callbacks.currentContextRoots, hasLength(2));
-
- expect(callbacks.currentContextRoots, contains(projPath));
- var projSources = callbacks.currentFileSources(projPath);
- expect(projSources, hasLength(1));
- expect(projSources.first.uri.toString(), 'package:proj/main.dart');
-
- expect(callbacks.currentContextRoots, contains(examplePath));
- var exampleSources = callbacks.currentFileSources(examplePath);
- expect(exampleSources, hasLength(1));
- expect(exampleSources.first.uri, toUri('$examplePath/example.dart'));
- }
-
- void test_setRoots_addFolderWithoutPubspec() {
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_addFolderWithPackagespec() {
- var packagespecPath = join(projPath, '.packages');
- var testLib = convertPath('/home/somebody/.pub/cache/unittest-0.9.9/lib');
- newFile(packagespecPath, content: 'unittest:${toUriStr(testLib)}');
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- var mainFile = newFile('$libPath/main.dart');
- var source = mainFile.createSource();
-
- manager.setRoots(<String>[projPath], <String>[]);
-
- // verify
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- expect(callbacks.currentFilePaths, hasLength(1));
-
- // smoketest resolution
- var resolvedSource =
- sourceFactory.resolveUri(source, 'package:unittest/unittest.dart');
- expect(resolvedSource, isNotNull);
- expect(resolvedSource.fullName, convertPath('$testLib/unittest.dart'));
- }
-
- void test_setRoots_addFolderWithPubspec() {
- var pubspecPath = join(projPath, 'pubspec.yaml');
- newFile(pubspecPath, content: 'pubspec');
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_addFolderWithPubspec_andPackagespec() {
- var pubspecPath = join(projPath, 'pubspec.yaml');
- var packagespecPath = join(projPath, '.packages');
- newFile(pubspecPath, content: 'pubspec');
- newFile(packagespecPath, content: '');
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- callbacks.assertContextPaths([projPath]);
- }
-
- void test_setRoots_addFolderWithPubspecAndLib() {
- var binPath = '$projPath/${ContextManagerTest.BIN_NAME}';
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- var srcPath = '$libPath/${ContextManagerTest.SRC_NAME}';
- var testPath = '$projPath/${ContextManagerTest.TEST_NAME}';
-
- newFile('$projPath/${ContextManagerImpl.PUBSPEC_NAME}');
- newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'proj:lib/');
- var appPath = newFile('$binPath/app.dart').path;
- newFile('$libPath/main.dart');
- newFile('$srcPath/internal.dart');
- var testFilePath = newFile('$testPath/main_test.dart').path;
-
- manager.setRoots(<String>[projPath], <String>[]);
- var sources = callbacks.currentFileSources(projPath);
-
- expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
- expect(sources, hasLength(4));
- var uris = sources.map((Source source) => source.uri.toString()).toList();
- expect(uris, contains(toUriStr(appPath)));
- expect(uris, contains('package:proj/main.dart'));
- expect(uris, contains('package:proj/src/internal.dart'));
- expect(uris, contains(toUriStr(testFilePath)));
- }
-
- void test_setRoots_addFolderWithPubspecAndPackagespecFolders() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProjectA = convertPath('$root/sub/aaa');
- var subProjectB = convertPath('$root/sub/sub2/bbb');
- var subProjectA_file = convertPath('$subProjectA/bin/a.dart');
- var subProjectB_file = convertPath('$subProjectB/bin/b.dart');
- // create files
- newFile('$subProjectA/pubspec.yaml', content: 'pubspec');
- newFile('$subProjectB/pubspec.yaml', content: 'pubspec');
- newFile('$subProjectA/.packages');
- newFile('$subProjectB/.packages');
-
- newFile(rootFile, content: 'library root;');
- newFile(subProjectA_file, content: 'library a;');
- newFile(subProjectB_file, content: 'library b;');
-
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProjectA, subProjectB]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProjectA, [subProjectA_file]);
- callbacks.assertContextFiles(subProjectB, [subProjectB_file]);
- }
-
- void test_setRoots_addFolderWithPubspecFolders() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var projectA = convertPath('$root/sub/aaa');
- var projectALib = convertPath('$root/sub/aaa/lib');
- var subProjectA_file = convertPath('$projectA/bin/a.dart');
- var projectB = convertPath('$root/sub/sub2/bbb');
- var projectBLib = convertPath('$root/sub/sub2/bbb/lib');
- var subProjectB_file = convertPath('$projectB/bin/b.dart');
- // create files
- newFile('$projectA/${ContextManagerImpl.PUBSPEC_NAME}');
- newFile('$projectA/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'foo:lib/');
- newFile('$projectB/${ContextManagerImpl.PUBSPEC_NAME}');
- newFile('$projectB/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'bar:lib/');
- newFile(rootFile, content: 'library root;');
- newFile(subProjectA_file, content: 'library a;');
- newFile(subProjectB_file, content: 'library b;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, projectA, projectB]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(projectA, [subProjectA_file]);
- callbacks.assertContextFiles(projectB, [subProjectB_file]);
- // verify package maps
- expect(_packageMap(root), isEmpty);
- expect(
- _packageMap(projectA),
- equals({
- 'foo': [getFolder(projectALib)]
- }));
- expect(
- _packageMap(projectB),
- equals({
- 'bar': [getFolder(projectBLib)]
- }));
- }
-
- void test_setRoots_exclude_newRoot_withExcludedFile() {
- // prepare paths
- var project = convertPath('/project');
- var file1 = convertPath('$project/file1.dart');
- var file2 = convertPath('$project/file2.dart');
- // create files
- newFile(file1, content: '// 1');
- newFile(file2, content: '// 2');
- // set roots
- manager.setRoots(<String>[project], <String>[file1]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file2]);
- }
-
- void test_setRoots_exclude_newRoot_withExcludedFolder() {
- // prepare paths
- var project = convertPath('/project');
- var folderA = convertPath('$project/aaa');
- var folderB = convertPath('$project/bbb');
- var fileA = convertPath('$folderA/a.dart');
- var fileB = convertPath('$folderB/b.dart');
- // create files
- newFile(fileA, content: 'library a;');
- newFile(fileB, content: 'library b;');
- // set roots
- manager.setRoots(<String>[project], <String>[folderB]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- }
-
- void test_setRoots_exclude_sameRoot_addExcludedFile() {
- // prepare paths
- var project = convertPath('/project');
- var file1 = convertPath('$project/file1.dart');
- var file2 = convertPath('$project/file2.dart');
- // create files
- newFile(file1, content: '// 1');
- newFile(file2, content: '// 2');
- // set roots
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file1, file2]);
- // exclude "2"
- manager.setRoots(<String>[project], <String>[file2]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file1]);
- }
-
- void test_setRoots_exclude_sameRoot_addExcludedFolder() {
- // prepare paths
- var project = convertPath('/project');
- var folderA = convertPath('$project/aaa');
- var folderB = convertPath('$project/bbb');
- var fileA = convertPath('$folderA/a.dart');
- var fileB = convertPath('$folderB/b.dart');
- // create files
- newFile(fileA, content: 'library a;');
- newFile(fileB, content: 'library b;');
- // initially both "aaa/a" and "bbb/b" are included
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA, fileB]);
- // exclude "bbb/"
- manager.setRoots(<String>[project], <String>[folderB]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- }
-
- void test_setRoots_exclude_sameRoot_removeExcludedFile() {
- // prepare paths
- var project = convertPath('/project');
- var file1 = convertPath('$project/file1.dart');
- var file2 = convertPath('$project/file2.dart');
- // create files
- newFile(file1, content: '// 1');
- newFile(file2, content: '// 2');
- // set roots
- manager.setRoots(<String>[project], <String>[file2]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file1]);
- // stop excluding "2"
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file1, file2]);
- }
-
- void test_setRoots_exclude_sameRoot_removeExcludedFile_inFolder() {
- // prepare paths
- var project = convertPath('/project');
- var file1 = convertPath('$project/bin/file1.dart');
- var file2 = convertPath('$project/bin/file2.dart');
- // create files
- newFile(file1, content: '// 1');
- newFile(file2, content: '// 2');
- // set roots
- manager.setRoots(<String>[project], <String>[file2]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file1]);
- // stop excluding "2"
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [file1, file2]);
- }
-
- void test_setRoots_exclude_sameRoot_removeExcludedFolder() {
- // prepare paths
- var project = convertPath('/project');
- var folderA = convertPath('$project/aaa');
- var folderB = convertPath('$project/bbb');
- var fileA = convertPath('$folderA/a.dart');
- var fileB = convertPath('$folderB/b.dart');
- // create files
- newFile(fileA, content: 'library a;');
- newFile(fileB, content: 'library b;');
- // exclude "bbb/"
- manager.setRoots(<String>[project], <String>[folderB]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- // stop excluding "bbb/"
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA, fileB]);
- }
-
- void test_setRoots_ignoreDocFolder() {
- var project = convertPath('/project');
- var fileA = convertPath('$project/foo.dart');
- var fileB = convertPath('$project/lib/doc/bar.dart');
- var fileC = convertPath('$project/doc/bar.dart');
- newFile(fileA, content: '');
- newFile(fileB, content: '');
- newFile(fileC, content: '');
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA, fileB]);
- }
-
- void test_setRoots_nested_includedByOuter_innerFirst() {
- var project = convertPath('/project');
- var projectPubspec = convertPath('$project/pubspec.yaml');
- var example = convertPath('$project/example');
- var examplePubspec = convertPath('$example/pubspec.yaml');
- // create files
- newFile(projectPubspec, content: 'name: project');
- newFile(examplePubspec, content: 'name: example');
- manager.setRoots(<String>[example, project], <String>[]);
- // verify
- {
- var rootInfo = manager.rootInfo;
- expect(rootInfo.children, hasLength(1));
- {
- var projectInfo = rootInfo.children[0];
- expect(projectInfo.folder.path, project);
- expect(projectInfo.children, hasLength(1));
- {
- var exampleInfo = projectInfo.children[0];
- expect(exampleInfo.folder.path, example);
- expect(exampleInfo.children, isEmpty);
- }
- }
- }
- expect(callbacks.currentContextRoots, unorderedEquals([project, example]));
- }
-
- void test_setRoots_nested_includedByOuter_outerPubspec() {
- var project = convertPath('/project');
- var projectPubspec = convertPath('$project/pubspec.yaml');
- var example = convertPath('$project/example');
- // create files
- newFile(projectPubspec, content: 'name: project');
- newFolder(example);
- manager.setRoots(<String>[project, example], <String>[]);
- // verify
- {
- var rootInfo = manager.rootInfo;
- expect(rootInfo.children, hasLength(1));
- {
- var projectInfo = rootInfo.children[0];
- expect(projectInfo.folder.path, project);
- expect(projectInfo.children, isEmpty);
- }
- }
- expect(callbacks.currentContextRoots, unorderedEquals([project]));
- }
-
- void test_setRoots_nested_includedByOuter_twoPubspecs() {
- var project = convertPath('/project');
- var projectPubspec = convertPath('$project/pubspec.yaml');
- var example = convertPath('$project/example');
- var examplePubspec = convertPath('$example/pubspec.yaml');
- // create files
- newFile(projectPubspec, content: 'name: project');
- newFile(examplePubspec, content: 'name: example');
- manager.setRoots(<String>[project, example], <String>[]);
- // verify
- {
- var rootInfo = manager.rootInfo;
- expect(rootInfo.children, hasLength(1));
- {
- var projectInfo = rootInfo.children[0];
- expect(projectInfo.folder.path, project);
- expect(projectInfo.children, hasLength(1));
- {
- var exampleInfo = projectInfo.children[0];
- expect(exampleInfo.folder.path, example);
- expect(exampleInfo.children, isEmpty);
- }
- }
- }
- expect(callbacks.currentContextRoots, unorderedEquals([project, example]));
- }
-
- void test_setRoots_newlyAddedFoldersGetProperPackageMap() {
- var packagePath = convertPath('/package/foo');
- newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'foo:${toUriStr('/package/foo')}');
- var packageFolder = newFolder(packagePath);
- manager.setRoots(<String>[projPath], <String>[]);
- expect(
- _currentPackageMap,
- equals({
- 'foo': [packageFolder]
- }));
- }
-
- void test_setRoots_noContext_excludedFolder() {
- // prepare paths
- var project = convertPath('/project');
- var excludedFolder = convertPath('$project/excluded');
- var excludedPubspec = convertPath('$excludedFolder/pubspec.yaml');
- // create files
- newFile(excludedPubspec, content: 'name: ignore-me');
- // set "/project", and exclude "/project/excluded"
- manager.setRoots(<String>[project], <String>[excludedFolder]);
- callbacks.assertContextPaths([project]);
- }
-
- void test_setRoots_noContext_inDotFolder() {
- var pubspecPath = join(projPath, '.pub', 'pubspec.yaml');
- newFile(pubspecPath, content: 'name: test');
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- expect(callbacks.currentContextRoots, hasLength(1));
- expect(callbacks.currentContextRoots, contains(projPath));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_packageResolver() {
- var filePath = join(projPath, 'lib', 'foo.dart');
- newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'foo:lib/');
- newFile(filePath, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
-
- var drivers = manager.getDriversInAnalysisRoot(newFolder(projPath));
- expect(drivers, hasLength(1));
- expect(drivers[0], isNotNull);
- var result = sourceFactory.forUri('package:foo/foo.dart');
- expect(result.fullName, filePath);
- }
-
- void test_setRoots_packagesFolder_hasContext() {
- var pubspecPath = join(projPath, 'packages', 'pubspec.yaml');
- newFile(pubspecPath, content: 'name: test');
- manager.setRoots(<String>[projPath], <String>[]);
- // verify
- expect(callbacks.currentContextRoots, hasLength(2));
- expect(callbacks.currentContextRoots, contains(projPath));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_pathContainsDotFile() {
- // If the path to a file (relative to the context root) contains a folder
- // whose name begins with '.', then the file is ignored.
- var project = convertPath('/project');
- var fileA = convertPath('$project/foo.dart');
- var fileB = convertPath('$project/.pub/bar.dart');
- newFile(fileA, content: '');
- newFile(fileB, content: '');
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- }
-
- void test_setRoots_removeFolderWithoutPubspec() {
- // add one root - there is a context
- manager.setRoots(<String>[projPath], <String>[]);
- expect(callbacks.currentContextRoots, hasLength(1));
- // set empty roots - no contexts
- manager.setRoots(<String>[], <String>[]);
- expect(callbacks.currentContextRoots, hasLength(0));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_removeFolderWithPackagespec() {
- // create a pubspec
- var pubspecPath = join(projPath, '.packages');
- newFile(pubspecPath, content: '');
- // add one root - there is a context
- manager.setRoots(<String>[projPath], <String>[]);
- expect(manager.changeSubscriptions, hasLength(1));
- expect(callbacks.currentContextRoots, hasLength(1));
- // set empty roots - no contexts
- manager.setRoots(<String>[], <String>[]);
- expect(manager.changeSubscriptions, hasLength(0));
- expect(callbacks.currentContextRoots, hasLength(0));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_removeFolderWithPackagespecFolder() {
- // prepare paths
- var projectA = convertPath('/projectA');
- var projectB = convertPath('/projectB');
- var subProjectA = convertPath('$projectA/sub');
- var subProjectB = convertPath('$projectB/sub');
- var projectA_file = convertPath('$projectA/a.dart');
- var projectB_file = convertPath('$projectB/a.dart');
- var subProjectA_pubspec = convertPath('$subProjectA/.packages');
- var subProjectB_pubspec = convertPath('$subProjectB/.packages');
- var subProjectA_file = convertPath('$subProjectA/bin/sub_a.dart');
- var subProjectB_file = convertPath('$subProjectB/bin/sub_b.dart');
- // create files
- newFile(projectA_file, content: '// a');
- newFile(projectB_file, content: '// b');
- newFile(subProjectA_pubspec, content: '');
- newFile(subProjectB_pubspec, content: '');
- newFile(subProjectA_file, content: '// sub-a');
- newFile(subProjectB_file, content: '// sub-b');
- // set roots
- manager.setRoots(<String>[projectA, projectB], <String>[]);
- callbacks
- .assertContextPaths([projectA, subProjectA, projectB, subProjectB]);
- callbacks.assertContextFiles(projectA, [projectA_file]);
- callbacks.assertContextFiles(projectB, [projectB_file]);
- callbacks.assertContextFiles(subProjectA, [subProjectA_file]);
- callbacks.assertContextFiles(subProjectB, [subProjectB_file]);
- // remove "projectB"
- manager.setRoots(<String>[projectA], <String>[]);
- callbacks.assertContextPaths([projectA, subProjectA]);
- callbacks.assertContextFiles(projectA, [projectA_file]);
- callbacks.assertContextFiles(subProjectA, [subProjectA_file]);
- }
-
- void test_setRoots_removeFolderWithPubspec() {
- // create a pubspec
- var pubspecPath = join(projPath, 'pubspec.yaml');
- newFile(pubspecPath, content: 'pubspec');
- // add one root - there is a context
- manager.setRoots(<String>[projPath], <String>[]);
- expect(callbacks.currentContextRoots, hasLength(1));
- // set empty roots - no contexts
- manager.setRoots(<String>[], <String>[]);
- expect(callbacks.currentContextRoots, hasLength(0));
- expect(callbacks.currentFilePaths, hasLength(0));
- }
-
- void test_setRoots_removeFolderWithPubspecFolder() {
- // prepare paths
- var projectA = convertPath('/projectA');
- var projectB = convertPath('/projectB');
- var subProjectA = convertPath('$projectA/sub');
- var subProjectB = convertPath('$projectB/sub');
- var projectA_file = convertPath('$projectA/a.dart');
- var projectB_file = convertPath('$projectB/a.dart');
- var subProjectA_pubspec = convertPath('$subProjectA/pubspec.yaml');
- var subProjectB_pubspec = convertPath('$subProjectB/pubspec.yaml');
- var subProjectA_file = convertPath('$subProjectA/bin/sub_a.dart');
- var subProjectB_file = convertPath('$subProjectB/bin/sub_b.dart');
- // create files
- newFile(projectA_file, content: '// a');
- newFile(projectB_file, content: '// b');
- newFile(subProjectA_pubspec, content: 'pubspec');
- newFile(subProjectB_pubspec, content: 'pubspec');
- newFile(subProjectA_file, content: '// sub-a');
- newFile(subProjectB_file, content: '// sub-b');
- // set roots
- manager.setRoots(<String>[projectA, projectB], <String>[]);
- callbacks
- .assertContextPaths([projectA, subProjectA, projectB, subProjectB]);
- callbacks.assertContextFiles(projectA, [projectA_file]);
- callbacks.assertContextFiles(projectB, [projectB_file]);
- callbacks.assertContextFiles(subProjectA, [subProjectA_file]);
- callbacks.assertContextFiles(subProjectB, [subProjectB_file]);
- // remove "projectB"
- manager.setRoots(<String>[projectA], <String>[]);
- callbacks.assertContextPaths([projectA, subProjectA]);
- callbacks.assertContextFiles(projectA, [projectA_file]);
- callbacks.assertContextFiles(subProjectA, [subProjectA_file]);
- }
-
- void test_setRoots_rootPathContainsDotFile() {
- // If the path to the context root itself contains a folder whose name
- // begins with '.', then that is not sufficient to cause any files in the
- // context to be ignored.
- var project = convertPath('/.pub/project');
- var fileA = convertPath('$project/foo.dart');
- newFile(fileA, content: '');
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- }
-
- Future<void> test_watch_addDummyLink() {
- manager.setRoots(<String>[projPath], <String>[]);
- // empty folder initially
- expect(callbacks.currentFilePaths, isEmpty);
- // add link
- var filePath = join(projPath, 'foo.dart');
- resourceProvider.newDummyLink(filePath);
- // the link was ignored
- return pumpEventQueue().then((_) {
- expect(callbacks.currentFilePaths, isEmpty);
- });
- }
-
- Future<void> test_watch_addFile() {
- manager.setRoots(<String>[projPath], <String>[]);
- // empty folder initially
- expect(callbacks.currentFilePaths, hasLength(0));
- // add file
- var filePath = join(projPath, 'foo.dart');
- newFile(filePath, content: 'contents');
- // the file was added
- return pumpEventQueue().then((_) {
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- });
- }
-
- Future<void> test_watch_addFile_excluded() {
- // prepare paths
- var project = convertPath('/project');
- var folderA = convertPath('$project/aaa');
- var folderB = convertPath('$project/bbb');
- var fileA = convertPath('$folderA/a.dart');
- var fileB = convertPath('$folderB/b.dart');
- // create files
- newFile(fileA, content: 'library a;');
- // set roots
- manager.setRoots(<String>[project], <String>[folderB]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- // add a file, ignored as excluded
- newFile(fileB, content: 'library b;');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- });
- }
-
- Future<void> test_watch_addFile_inDocFolder_inner() {
- // prepare paths
- var project = convertPath('/project');
- var fileA = convertPath('$project/a.dart');
- var fileB = convertPath('$project/lib/doc/b.dart');
- // create files
- newFile(fileA, content: '');
- // set roots
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- // add a "lib/doc" file, it is not ignored
- newFile(fileB, content: '');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA, fileB]);
- });
- }
-
- Future<void> test_watch_addFile_inDocFolder_topLevel() {
- // prepare paths
- var project = convertPath('/project');
- var fileA = convertPath('$project/a.dart');
- var fileB = convertPath('$project/doc/b.dart');
- // create files
- newFile(fileA, content: '');
- // set roots
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- // add a "doc" file, it is ignored
- newFile(fileB, content: '');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- });
- }
-
- Future<void> test_watch_addFile_pathContainsDotFile() async {
- // If a file is added and the path to it (relative to the context root)
- // contains a folder whose name begins with '.', then the file is ignored.
- var project = convertPath('/project');
- var fileA = convertPath('$project/foo.dart');
- var fileB = convertPath('$project/.pub/bar.dart');
- newFile(fileA, content: '');
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- newFile(fileB, content: '');
- await pumpEventQueue();
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- }
-
- Future<void> test_watch_addFile_rootPathContainsDotFile() async {
- // If a file is added and the path to the context contains a folder whose
- // name begins with '.', then the file is not ignored.
- var project = convertPath('/.pub/project');
- var fileA = convertPath('$project/foo.dart');
- var fileB = convertPath('$project/bar/baz.dart');
- newFile(fileA, content: '');
- manager.setRoots(<String>[project], <String>[]);
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA]);
- newFile(fileB, content: '');
- await pumpEventQueue();
- callbacks.assertContextPaths([project]);
- callbacks.assertContextFiles(project, [fileA, fileB]);
- }
-
- Future<void> test_watch_addFileInSubfolder() {
- manager.setRoots(<String>[projPath], <String>[]);
- // empty folder initially
- expect(callbacks.currentFilePaths, hasLength(0));
- // add file in subfolder
- var filePath = join(projPath, 'foo', 'bar.dart');
- newFile(filePath, content: 'contents');
- // the file was added
- return pumpEventQueue().then((_) {
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- });
- }
-
- Future<void> test_watch_addPackagespec_toRoot() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var rootPackagespec = convertPath('$root/.packages');
- // create files
- newFile(rootFile, content: 'library root;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- // add packagespec - still just one root
- newFile(rootPackagespec, content: '');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile]);
- // TODO(pquitslund): verify that a new source factory is created --
- // likely this will need to happen in a corresponding ServerContextManagerTest.
- });
- }
-
- Future<void> test_watch_addPackagespec_toSubFolder() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub/aaa');
- var subPubspec = convertPath('$subProject/.packages');
- var subFile = convertPath('$subProject/bin/a.dart');
- // create files
- newFile(rootFile, content: 'library root;');
- newFile(subFile, content: 'library a;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile, subFile]);
- // add .packages
- newFile(subPubspec, content: '');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- });
- }
-
- Future<void> test_watch_addPackagespec_toSubFolder_ofSubFolder() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub');
- var subPubspec = convertPath('$subProject/.packages');
- var subFile = convertPath('$subProject/bin/sub.dart');
- var subSubPubspec = convertPath('$subProject/subsub/.packages');
- // create files
- newFile(rootFile, content: 'library root;');
- newFile(subPubspec, content: '');
- newFile(subFile, content: 'library sub;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- // add pubspec - ignore, because is already in a packagespec-based context
- newFile(subSubPubspec, content: '');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- });
- }
-
- Future<void> test_watch_addPackagespec_toSubFolder_withPubspec() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub/aaa');
- var subPackagespec = convertPath('$subProject/.packages');
- var subPubspec = convertPath('$subProject/pubspec.yaml');
- var subFile = convertPath('$subProject/bin/a.dart');
- // create files
- newFile(subPubspec, content: 'pubspec');
- newFile(rootFile, content: 'library root;');
- newFile(subFile, content: 'library a;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProject]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
-
- // add .packages
- newFile(subPackagespec, content: '');
- return pumpEventQueue().then((_) {
- // Should NOT create another context.
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- });
- }
-
- Future<void> test_watch_addPubspec_toRoot() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var rootPubspec = convertPath('$root/pubspec.yaml');
- // create files
- newFile(rootFile, content: 'library root;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- // add pubspec - still just one root
- newFile(rootPubspec, content: 'pubspec');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile]);
- });
- }
-
- Future<void> test_watch_addPubspec_toSubFolder() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub/aaa');
- var subPubspec = convertPath('$subProject/pubspec.yaml');
- var subFile = convertPath('$subProject/bin/a.dart');
- // create files
- newFile(rootFile, content: 'library root;');
- newFile(subFile, content: 'library a;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile, subFile]);
- // add pubspec
- newFile(subPubspec, content: 'pubspec');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- });
- }
-
- Future<void> test_watch_addPubspec_toSubFolder_ofSubFolder() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub');
- var subPubspec = convertPath('$subProject/pubspec.yaml');
- var subFile = convertPath('$subProject/bin/sub.dart');
- var subSubPubspec = convertPath('$subProject/subsub/pubspec.yaml');
- // create files
- newFile(rootFile, content: 'library root;');
- newFile(subPubspec, content: 'pubspec');
- newFile(subFile, content: 'library sub;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- // add pubspec - ignore, because is already in a pubspec-based context
- newFile(subSubPubspec, content: 'pubspec');
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- });
- }
-
- Future<void> test_watch_deleteFile() {
- var filePath = join(projPath, 'foo.dart');
- // add root with a file
- var file = newFile(filePath, content: 'contents');
- var projFolder = file.parent2;
- manager.setRoots(<String>[projPath], <String>[]);
- // the file was added
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- expect(file.exists, isTrue);
- expect(projFolder.exists, isTrue);
- // delete the file
- deleteFile(filePath);
- return pumpEventQueue().then((_) {
- expect(file.exists, isFalse);
- expect(projFolder.exists, isTrue);
- expect(callbacks.currentFilePaths, hasLength(0));
- });
- }
-
- Future<void> test_watch_deleteFolder() {
- var filePath = join(projPath, 'foo.dart');
- // add root with a file
- var file = newFile(filePath, content: 'contents');
- var projFolder = file.parent2;
- manager.setRoots(<String>[projPath], <String>[]);
- // the file was added
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- expect(file.exists, isTrue);
- expect(projFolder.exists, isTrue);
- // delete the folder
- deleteFolder(projPath);
- return pumpEventQueue().then((_) {
- expect(file.exists, isFalse);
- expect(projFolder.exists, isFalse);
- expect(callbacks.currentFilePaths, hasLength(0));
- });
- }
-
- Future<void> test_watch_deletePackagespec_fromRoot() {
- // prepare paths
- var root = convertPath('/root');
- var rootPubspec = convertPath('$root/.packages');
- var rootFile = convertPath('$root/root.dart');
- // create files
- newFile(rootPubspec, content: '');
- newFile(rootFile, content: 'library root;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile]);
- // delete the pubspec
- deleteFile(rootPubspec);
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile]);
- });
- }
-
- Future<void> test_watch_deletePackagespec_fromSubFolder() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub/aaa');
- var subPubspec = convertPath('$subProject/.packages');
- var subFile = convertPath('$subProject/bin/a.dart');
- // create files
- newFile(subPubspec, content: '');
- newFile(rootFile, content: 'library root;');
- newFile(subFile, content: 'library a;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProject]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- // delete the pubspec
- deleteFile(subPubspec);
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile, subFile]);
- });
- }
-
- Future<void> test_watch_deletePackagespec_fromSubFolder_withPubspec() {
- // prepare paths:
- //
- // root
- // root.dart
- // sub
- // aaa
- // .packages
- // pubspec.yaml
- // bin
- // a.dart
- //
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub/aaa');
- var subPackagespec = convertPath('$subProject/.packages');
- var subPubspec = convertPath('$subProject/pubspec.yaml');
- var subFile = convertPath('$subProject/bin/a.dart');
- // create files
- newFile(subPackagespec, content: '');
- newFile(subPubspec, content: 'pubspec');
- newFile(rootFile, content: 'library root;');
- newFile(subFile, content: 'library a;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProject]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- // delete the packagespec
- deleteFile(subPackagespec);
- return pumpEventQueue().then((_) {
- // Should NOT merge
- callbacks.assertContextPaths([root, subProject]);
- callbacks.assertContextFiles(subProject, [subFile]);
- });
- }
-
- Future<void> test_watch_deletePubspec_fromRoot() {
- // prepare paths
- var root = convertPath('/root');
- var rootPubspec = convertPath('$root/pubspec.yaml');
- var rootFile = convertPath('$root/root.dart');
- // create files
- newFile(rootPubspec, content: 'pubspec');
- newFile(rootFile, content: 'library root;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile]);
- // delete the pubspec
- deleteFile(rootPubspec);
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile]);
- });
- }
-
- Future<void> test_watch_deletePubspec_fromSubFolder() {
- // prepare paths
- var root = convertPath('/root');
- var rootFile = convertPath('$root/root.dart');
- var subProject = convertPath('$root/sub/aaa');
- var subPubspec = convertPath('$subProject/pubspec.yaml');
- var subFile = convertPath('$subProject/bin/a.dart');
- // create files
- newFile(subPubspec, content: 'pubspec');
- newFile(rootFile, content: 'library root;');
- newFile(subFile, content: 'library a;');
- // set roots
- manager.setRoots(<String>[root], <String>[]);
- callbacks.assertContextPaths([root, subProject]);
- // verify files
- callbacks.assertContextFiles(root, [rootFile]);
- callbacks.assertContextFiles(subProject, [subFile]);
- // delete the pubspec
- deleteFile(subPubspec);
- return pumpEventQueue().then((_) {
- callbacks.assertContextPaths([root]);
- callbacks.assertContextFiles(root, [rootFile, subFile]);
- });
- }
-
- Future<void> test_watch_modifyFile() {
- var filePath = join(projPath, 'foo.dart');
- // add root with a file
- newFile(filePath, content: 'contents');
- manager.setRoots(<String>[projPath], <String>[]);
- // the file was added
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- // TODO(brianwilkerson) Test when the file was modified
- // update the file
- callbacks.now++;
- modifyFile(filePath, 'new contents');
- return pumpEventQueue().then((_) {
- // TODO(brianwilkerson) Test when the file was modified
- });
- }
-
- Future<void> test_watch_modifyPackageConfigJson() {
- var packageConfigPath = '$projPath/.dart_tool/package_config.json';
- var filePath = convertPath('$projPath/bin/main.dart');
-
- newFile(packageConfigPath, content: '');
- newFile(filePath, content: 'library main;');
-
- manager.setRoots(<String>[projPath], <String>[]);
-
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- expect(_currentPackageMap, isEmpty);
-
- // update .dart_tool/package_config.json
- callbacks.now++;
- modifyFile(
- packageConfigPath,
- (PackageConfigFileBuilder()..add(name: 'my', rootPath: '../'))
- .toContent(toUriStr: toUriStr),
- );
-
- return pumpEventQueue().then((_) {
- // verify new package info
- expect(_currentPackageMap.keys, unorderedEquals(['my']));
- });
- }
-
- Future<void> test_watch_modifyPackagespec() {
- var packagesPath = convertPath('$projPath/.packages');
- var filePath = convertPath('$projPath/bin/main.dart');
-
- newFile(packagesPath, content: '');
- newFile(filePath, content: 'library main;');
-
- manager.setRoots(<String>[projPath], <String>[]);
-
- var filePaths = callbacks.currentFilePaths;
- expect(filePaths, hasLength(1));
- expect(filePaths, contains(filePath));
- expect(_currentPackageMap, isEmpty);
-
- // update .packages
- callbacks.now++;
- modifyFile(packagesPath, 'main:./lib/');
- return pumpEventQueue().then((_) {
- // verify new package info
- expect(_currentPackageMap.keys, unorderedEquals(['main']));
- });
- }
-}
-
-abstract class ContextManagerTest with ResourceProviderMixin {
- /// The name of the 'bin' directory.
- static const String BIN_NAME = 'bin';
-
- /// The name of the 'example' directory.
- static const String EXAMPLE_NAME = 'example';
-
- /// The name of the 'lib' directory.
- static const String LIB_NAME = 'lib';
-
- /// The name of the 'src' directory.
- static const String SRC_NAME = 'src';
-
- /// The name of the 'test' directory.
- static const String TEST_NAME = 'test';
-
- ContextManagerImpl manager;
-
- TestContextManagerCallbacks callbacks;
-
- String projPath;
-
- AnalysisError missing_return =
- AnalysisError(_MockSource('test.dart'), 0, 1, HintCode.MISSING_RETURN, [
- ['x']
- ]);
-
- AnalysisError invalid_assignment_error = AnalysisError(
- _MockSource('test.dart'), 0, 1, CompileTimeErrorCode.INVALID_ASSIGNMENT, [
- ['x'],
- ['y']
- ]);
-
- AnalysisError unused_local_variable = AnalysisError(
- _MockSource('test.dart'), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
- ['x']
- ]);
-
- List<Glob> get analysisFilesGlobs {
- var patterns = <String>[
- '**/*.${AnalysisEngine.SUFFIX_DART}',
- '**/${AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE}'
- ];
- return patterns
- .map((pattern) => Glob(path.posix.separator, pattern))
- .toList();
- }
-
- AnalysisOptions get analysisOptions => callbacks.analysisOptions;
-
- List<ErrorProcessor> get errorProcessors => analysisOptions.errorProcessors;
-
- List<Linter> get lints => analysisOptions.lintRules;
-
- SourceFactory get sourceFactory => callbacks.sourceFactory;
-
- Map<String, List<Folder>> get _currentPackageMap => _packageMap(projPath);
-
- /// TODO(brianwilkerson) This doesn't add the strong mode processor when using
- /// the new analysis driver.
- ErrorProcessor getProcessor(AnalysisError error) => errorProcessors
- .firstWhere((ErrorProcessor p) => p.appliesTo(error), orElse: () => null);
-
- void processRequiredPlugins() {
- registerLintRules();
- }
-
- void setUp() {
- processRequiredPlugins();
- projPath = convertPath('/my/proj');
- newFolder(projPath);
- // Create an SDK in the mock file system.
- MockSdk(resourceProvider: resourceProvider);
- var sdkManager = DartSdkManager(convertPath(sdkRoot));
- manager = ContextManagerImpl(
- resourceProvider,
- sdkManager,
- analysisFilesGlobs,
- InstrumentationService.NULL_SERVICE,
- );
- var logger = PerformanceLog(NullStringSink());
- var scheduler = AnalysisDriverScheduler(logger);
- callbacks = TestContextManagerCallbacks(
- resourceProvider, sdkManager, logger, scheduler);
- manager.callbacks = callbacks;
- }
-
- Map<String, List<Folder>> _packageMap(String contextPath) {
- var folder = getFolder(contextPath);
- var info = manager.getContextInfoFor(folder);
- return info.analysisDriver.sourceFactory?.packageMap;
- }
-}
-
-@reflectiveTest
-class ContextManagerWithOptionsTest extends ContextManagerTest {
- String get optionsFileName => AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE;
-
- void deleteOptionsFile() {
- deleteFile('$projPath/$optionsFileName');
- }
-
- Future<void> test_analysis_options_file_delete() async {
- // Setup analysis options
- newFile('$projPath/$optionsFileName', content: r'''
-embedded_libs:
- "dart:foobar": "../sdk_ext/entry.dart"
-analyzer:
- errors:
- unused_local_variable: false
-linter:
- rules:
- - camel_case_types
-''');
-
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
-
- // Verify options were set.
- expect(errorProcessors, hasLength(1));
- expect(lints, hasLength(1));
-
- // Remove options.
- deleteOptionsFile();
- await pumpEventQueue();
-
- // Verify defaults restored.
- expect(errorProcessors, isEmpty);
- expect(lints, isEmpty);
- }
-
- @failingTest
- Future<void> test_analysis_options_file_delete_with_embedder() async {
- // This fails because the ContextBuilder doesn't pick up the strongMode
- // flag from the embedder.yaml file.
- // Setup _embedder.yaml.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/_embedder.yaml', content: r'''
-analyzer:
- language:
- enablePreviewDart2: true
- errors:
- missing_return: false
-linter:
- rules:
- - avoid_as
-''');
-
- // Setup .packages file
- newFile('$projPath/.packages', content: r'''
-test_pack:lib/''');
-
- // Setup analysis options
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- errors:
- unused_local_variable: false
-linter:
- rules:
- - camel_case_types
-''');
-
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
-
- // Verify options were set.
- expect(errorProcessors, hasLength(2));
- expect(lints, hasLength(2));
-
- // Remove options.
- deleteOptionsFile();
- await pumpEventQueue();
-
- // Verify defaults restored.
- expect(lints, hasLength(1));
- expect(lints.first, const TypeMatcher<AvoidAs>());
- expect(errorProcessors, hasLength(1));
- expect(getProcessor(missing_return).severity, isNull);
- }
-
- Future<void> test_analysis_options_include() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$sdkExtPath/entry.dart');
- var sdkExtSrcPath = '$projPath/sdk_ext/src';
- newFile('$sdkExtSrcPath/part.dart');
- // Setup analysis options file which includes another options file.
- newFile('$projPath/$optionsFileName', content: r'''
-include: other_options.yaml
-''');
- newFile('$projPath/other_options.yaml', content: r'''
-analyzer:
- errors:
- unused_local_variable: false
-linter:
- rules:
- - camel_case_types
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
- // Verify options were set.
- expect(errorProcessors, hasLength(1));
- expect(lints, hasLength(1));
- expect(lints[0].name, 'camel_case_types');
- }
-
- Future<void> test_analysis_options_include_package() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$sdkExtPath/entry.dart');
- var sdkExtSrcPath = '$projPath/sdk_ext/src';
- newFile('$sdkExtSrcPath/part.dart');
- // Setup package
- var booLibPosixPath = '/my/pkg/boo/lib';
- newFile('$booLibPosixPath/other_options.yaml', content: r'''
-analyzer:
- errors:
- unused_local_variable: false
-linter:
- rules:
- - camel_case_types
-''');
- // Setup analysis options file which includes another options file.
- newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
- content: 'boo:${toUriStr(booLibPosixPath)}\n');
- newFile('$projPath/$optionsFileName', content: r'''
-include: package:boo/other_options.yaml
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
- // Verify options were set.
- expect(errorProcessors, hasLength(1));
- expect(lints, hasLength(1));
- expect(lints[0].name, 'camel_case_types');
- }
-
- @failingTest
- Future<void> test_analysis_options_parse_failure() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$sdkExtPath/entry.dart');
- var sdkExtSrcPath = '$projPath/sdk_ext/src';
- newFile('$sdkExtSrcPath/part.dart');
- // Setup analysis options file with ignore list.
- var optionsFilePath = newFile('$projPath/$optionsFileName', content: r'''
-;
-''').path;
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Check that an error was produced.
- TestNotificationManager notificationManager = callbacks.notificationManager;
- var errors = notificationManager.recordedErrors;
- expect(errors, hasLength(1));
- expect(errors[errors.keys.first][optionsFilePath], hasLength(1));
- }
-
- Future<void> test_deleteRoot_hasAnalysisOptions() async {
- newFile('$projPath/$optionsFileName');
-
- // Add the root.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
-
- // Remove the root, with the analysis options file.
- // No exceptions.
- deleteFolder(projPath);
- await pumpEventQueue();
- }
-
- @failingTest
- Future<void> test_embedder_options() async {
- // This fails because the ContextBuilder doesn't pick up the strongMode
- // flag from the embedder.yaml file.
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$projPath/test', content: 'test.dart');
- newFile('$sdkExtPath/entry.dart');
- // Setup _embedder.yaml.
- newFile('$libPath/_embedder.yaml', content: r'''
-embedded_libs:
- "dart:foobar": "../sdk_ext/entry.dart"
-analyzer:
- strong-mode: true
- errors:
- missing_return: false
-linter:
- rules:
- - avoid_as
-''');
- // Setup .packages file
- newFile('$projPath/.packages', content: r'''
-test_pack:lib/''');
-
- // Setup analysis options
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - 'test/**'
- errors:
- unused_local_variable: false
-linter:
- rules:
- - camel_case_types
-''');
-
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
-
- // Confirm that one context was created.
- var count = manager.numberOfContextsInAnalysisRoot(newFolder(projPath));
- expect(count, equals(1));
-
- // Verify options.
- // * from `_embedder.yaml`:
- // TODO(brianwilkerson) Figure out what to use in place of 'strongMode'.
-// expect(analysisOptions.strongMode, isTrue);
-
- // * verify tests are excluded
- expect(
- callbacks.currentContextFilePaths[projPath].keys,
- unorderedEquals(
- ['/my/proj/sdk_ext/entry.dart', '/my/proj/$optionsFileName']));
-
- // Verify filter setup.
- expect(errorProcessors, hasLength(2));
-
- // * (embedder.)
- expect(getProcessor(missing_return).severity, isNull);
-
- // * (options.)
- expect(getProcessor(unused_local_variable).severity, isNull);
-
- // Verify lints.
- var lintNames = lints.map((lint) => lint.name);
-
- expect(
- lintNames,
- unorderedEquals([
- 'avoid_as' /* embedder */,
- 'camel_case_types' /* options */
- ]));
-
- // Sanity check embedder libs.
- var source = sourceFactory.forUri('dart:foobar');
- expect(source, isNotNull);
- expect(source.fullName, '/my/proj/sdk_ext/entry.dart');
- }
-
- Future<void> test_error_filter_analysis_option() async {
- // Create files.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- errors:
- unused_local_variable: ignore
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Verify filter setup.
- expect(errorProcessors, hasLength(1));
- expect(getProcessor(unused_local_variable).severity, isNull);
- }
-
- Future<void> test_error_filter_analysis_option_multiple_filters() async {
- // Create files.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- errors:
- invalid_assignment: ignore
- unused_local_variable: error
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Verify filter setup.
- expect(errorProcessors, hasLength(2));
-
- expect(getProcessor(invalid_assignment_error).severity, isNull);
- expect(getProcessor(unused_local_variable).severity, ErrorSeverity.ERROR);
- }
-
- Future<void> test_error_filter_analysis_option_synonyms() async {
- // Create files.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- errors:
- unused_local_variable: ignore
- ambiguous_import: false
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Verify filter setup.
- expect(errorProcessors, isNotNull);
- expect(errorProcessors, hasLength(2));
- }
-
- Future<void> test_error_filter_analysis_option_unpsecified() async {
- // Create files.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
-# errors:
-# unused_local_variable: ignore
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Verify filter setup.
- expect(errorProcessors, isEmpty);
- }
-
- Future<void> test_non_analyzable_files_not_considered() async {
- // Set up project and get a reference to the driver.
- manager.setRoots(<String>[projPath], <String>[]);
- var projectFolder = newFolder(projPath);
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, hasLength(1));
-
- // Add the driver to the manager so that it will receive the events.
- manager.driverMap[projectFolder] = drivers[0];
-
- // Ensure adding a file that shouldn't be analyzed is not picked up.
- newFile('$projPath/test.txt');
- await pumpEventQueue();
- expect(drivers[0].hasFilesToAnalyze, false);
-
- // Ensure modifying a file that shouldn't be analyzed is not picked up.
- modifyFile('$projPath/test.txt', 'new content');
- await pumpEventQueue();
- expect(drivers[0].hasFilesToAnalyze, false);
- }
-
- @failingTest
- Future<void> test_optionsFile_update_strongMode() async {
- // It appears that this fails because we are not correctly updating the
- // analysis options in the driver when the file is modified.
- //return super.test_optionsFile_update_strongMode();
- // After a few other changes, the test now times out on my machine, so I'm
- // disabling it in order to prevent it from being flaky.
- _fail('Test times out');
- var file = newFile('$projPath/bin/test.dart', content: r'''
-main() {
- var paths = <int>[];
- var names = <String>[];
- paths.addAll(names.map((s) => s.length));
-}
-''');
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- strong-mode: false
-''');
- // Create the context.
- manager.setRoots(<String>[projPath], <String>[]);
- await pumpEventQueue();
-
- var result = await callbacks.currentDriver.getResult(file.path);
-
- // Not strong mode - both in the context and the SDK context.
-// AnalysisContext sdkContext = sourceFactory.dartSdk.context;
- // TODO(brianwilkerson) Figure out whether there is an option other than
- // 'strongMode' that will apply to the SDK context.
-// expect(analysisOptions.strongMode, isFalse);
-// expect(sdkContext.analysisOptions.strongMode, isFalse);
- expect(result.errors, isEmpty);
-
- // Update the options file - turn on 'strong-mode'.
- modifyFile('$projPath/$optionsFileName', r'''
-analyzer:
- strong-mode: true
-''');
- await pumpEventQueue();
-
- // Strong mode - both in the context and the SDK context.
- result = await callbacks.currentDriver.getResult(file.path);
-
- // Not strong mode - both in the context and the SDK context.
-// sdkContext = sourceFactory.dartSdk.context;
- // TODO(brianwilkerson) Figure out whether there is an option other than
- // 'strongMode' that will apply to the SDK context.
-// expect(analysisOptions.strongMode, isTrue);
-// expect(sdkContext.analysisOptions.strongMode, isTrue);
- // The code is strong-mode clean.
- // Verify that TypeSystem was reset.
- expect(result.errors, isEmpty);
- }
-
- @failingTest
- Future<void> test_path_filter_analysis_option() async {
- // This fails because we're not analyzing the analysis options file.
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/nope.dart');
- var sdkExtPath = '$projPath/sdk_ext';
- newFile('$sdkExtPath/entry.dart');
- var sdkExtSrcPath = '$projPath/sdk_ext/src';
- newFile('$sdkExtSrcPath/part.dart');
- // Setup analysis options file with ignore list.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - lib/nope.dart
- - 'sdk_ext/**'
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Verify that analysis options was parsed and the ignore patterns applied.
- var projectFolder = newFolder(projPath);
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, hasLength(1));
- var driver = drivers[0];
- expect(
- driver.addedFiles,
- unorderedEquals(
- ['/my/proj/lib/main.dart', '/my/proj/$optionsFileName']));
- }
-
- Future<void> test_path_filter_child_contexts_option() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/pubspec.yaml', content: r'''
-name: foobar
-''');
- var otherLibPath = '$projPath/other_lib';
- newFile('$otherLibPath/entry.dart');
- newFile('$otherLibPath/pubspec.yaml', content: r'''
-name: other_lib
-''');
- // Setup analysis options file with ignore list that ignores the 'other_lib'
- // directory by name.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - 'other_lib'
-''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
- // Verify that the context in other_lib wasn't created and that the
- // context in lib was created.
- var projectFolder = newFolder(projPath);
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, hasLength(2));
- expect(drivers[0].name, equals(convertPath('/my/proj')));
- expect(drivers[1].name, equals(convertPath('/my/proj/lib')));
- }
-
- Future<void>
- test_path_filter_recursive_wildcard_child_contexts_option() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/pubspec.yaml', content: r'''
- name: foobar
- ''');
- var otherLibPath = '$projPath/other_lib';
- newFile('$otherLibPath/entry.dart');
- newFile('$otherLibPath/pubspec.yaml', content: r'''
- name: other_lib
- ''');
- // Setup analysis options file with ignore list that ignores 'other_lib'
- // and all descendants.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - 'other_lib/**'
- ''');
- // Setup context.
- manager.setRoots(<String>[projPath], <String>[]);
-
- // Verify that the context in other_lib wasn't created and that the
- // context in lib was created.
- var projectFolder = newFolder(projPath);
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, hasLength(2));
- expect(drivers[0].name, equals(convertPath('/my/proj')));
- expect(drivers[1].name, equals(convertPath('/my/proj/lib')));
- }
-
- Future<void> test_path_filter_wildcard_child_contexts_option() async {
- // Create files.
- var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
- newFile('$libPath/main.dart');
- newFile('$libPath/pubspec.yaml', content: r'''
-name: foobar
-''');
- var otherLibPath = '$projPath/other_lib';
- newFile('$otherLibPath/entry.dart');
- newFile('$otherLibPath/pubspec.yaml', content: r'''
-name: other_lib
-''');
- // Setup analysis options file with ignore list that ignores 'other_lib'
- // and all immediate children.
- newFile('$projPath/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - 'other_lib/*'
-''');
- // Setup context / driver.
- manager.setRoots(<String>[projPath], <String>[]);
-
- var projectFolder = newFolder(projPath);
- var drivers = manager.getDriversInAnalysisRoot(projectFolder);
- expect(drivers, hasLength(2));
- expect(drivers[0].name, equals(convertPath('/my/proj')));
- expect(drivers[1].name, equals(convertPath('/my/proj/lib')));
- }
-
- void test_setRoots_nested_excludedByOuter() {
- var project = convertPath('/project');
- var projectPubspec = convertPath('$project/pubspec.yaml');
- var example = convertPath('$project/example');
- var examplePubspec = convertPath('$example/pubspec.yaml');
- // create files
- newFile(projectPubspec, content: 'name: project');
- newFile(examplePubspec, content: 'name: example');
- newFile('$project/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - 'example'
-''');
- manager.setRoots(<String>[project, example], <String>[]);
- // verify
- {
- var rootInfo = manager.rootInfo;
- expect(rootInfo.children, hasLength(1));
- {
- var projectInfo = rootInfo.children[0];
- expect(projectInfo.folder.path, project);
- expect(projectInfo.children, hasLength(1));
- {
- var exampleInfo = projectInfo.children[0];
- expect(exampleInfo.folder.path, example);
- expect(exampleInfo.children, isEmpty);
- }
- }
- }
- expect(callbacks.currentContextRoots, hasLength(2));
- expect(callbacks.currentContextRoots, unorderedEquals([project, example]));
- }
-
- void test_setRoots_nested_excludedByOuter_deep() {
- var a = convertPath('/a');
- var c = convertPath('$a/b/c');
- var aPubspec = convertPath('$a/pubspec.yaml');
- var cPubspec = convertPath('$c/pubspec.yaml');
- // create files
- newFile(aPubspec, content: 'name: aaa');
- newFile(cPubspec, content: 'name: ccc');
- newFile('$a/$optionsFileName', content: r'''
-analyzer:
- exclude:
- - 'b**'
-''');
- manager.setRoots(<String>[a, c], <String>[]);
- // verify
- {
- var rootInfo = manager.rootInfo;
- expect(rootInfo.children, hasLength(1));
- {
- var aInfo = rootInfo.children[0];
- expect(aInfo.folder.path, a);
- expect(aInfo.children, hasLength(1));
- {
- var cInfo = aInfo.children[0];
- expect(cInfo.folder.path, c);
- expect(cInfo.children, isEmpty);
- }
- }
- }
- expect(callbacks.currentContextRoots, hasLength(2));
- expect(callbacks.currentContextRoots, unorderedEquals([a, c]));
- }
-
- Future<void> test_watchEvents() async {
- var libPath = newFolder('$projPath/${ContextManagerTest.LIB_NAME}').path;
- manager.setRoots(<String>[projPath], <String>[]);
- newFile('$libPath/main.dart');
- await Future.delayed(Duration(milliseconds: 1));
- expect(callbacks.watchEvents, hasLength(1));
- }
-}
-
-class TestContextManagerCallbacks extends ContextManagerCallbacks {
- /// Source of timestamps stored in [currentContextFilePaths].
- int now = 0;
-
- /// The analysis driver that was created.
- AnalysisDriver currentDriver;
-
- /// A table mapping paths to the analysis driver associated with that path.
- Map<String, AnalysisDriver> driverMap = <String, AnalysisDriver>{};
-
- /// Map from context to the timestamp when the context was created.
- Map<String, int> currentContextTimestamps = <String, int>{};
-
- /// Map from context to (map from file path to timestamp of last event).
- final Map<String, Map<String, int>> currentContextFilePaths =
- <String, Map<String, int>>{};
-
- /// A map from the paths of contexts to a set of the sources that should be
- /// explicitly analyzed in those contexts.
- final Map<String, Set<Source>> currentContextSources =
- <String, Set<Source>>{};
-
- /// Resource provider used for this test.
- final ResourceProvider resourceProvider;
-
- /// The manager managing the SDKs.
- final DartSdkManager sdkManager;
-
- /// The logger used by the scheduler and the driver.
- final PerformanceLog logger;
-
- /// The scheduler used by the driver.
- final AnalysisDriverScheduler scheduler;
-
- /// The list of `flushedFiles` in the last [removeContext] invocation.
- List<String> lastFlushedFiles;
-
- /// The watch events that have been broadcast.
- List<WatchEvent> watchEvents = <WatchEvent>[];
-
- @override
- AbstractNotificationManager notificationManager = TestNotificationManager();
-
- TestContextManagerCallbacks(
- this.resourceProvider, this.sdkManager, this.logger, this.scheduler);
-
- /// Return the current set of analysis options.
- AnalysisOptions get analysisOptions => currentDriver?.analysisOptions;
-
- /// Return the paths to the context roots that currently exist.
- Iterable<String> get currentContextRoots {
- return currentContextTimestamps.keys;
- }
-
- /// Return the paths to the files being analyzed in the current context root.
- Iterable<String> get currentFilePaths {
- if (currentDriver == null) {
- return <String>[];
- }
- return currentDriver.addedFiles;
- }
-
- /// Return the current source factory.
- SourceFactory get sourceFactory => currentDriver?.sourceFactory;
-
- @override
- AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot) {
- var path = folder.path;
- expect(currentContextRoots, isNot(contains(path)));
- expect(contextRoot, isNotNull);
- expect(contextRoot.root, path);
- currentContextTimestamps[path] = now;
-
- var builder = createContextBuilder(folder);
- builder.analysisDriverScheduler = scheduler;
- builder.byteStore = MemoryByteStore();
- builder.performanceLog = logger;
- builder.fileContentOverlay = FileContentOverlay();
- var workspace = ContextBuilder.createWorkspace(
- resourceProvider: resourceProvider,
- options: builder.builderOptions,
- rootPath: path,
- );
- currentDriver = builder.buildDriver(contextRoot, workspace);
-
- driverMap[path] = currentDriver;
- currentDriver.exceptions.listen((ExceptionResult result) {
- AnalysisEngine.instance.instrumentationService.logException(
- CaughtException.withMessage('Analysis failed: ${result.filePath}',
- result.exception.exception, result.exception.stackTrace));
- });
- return currentDriver;
- }
-
- @override
- void afterContextRefresh() {}
-
- @override
- void afterWatchEvent(WatchEvent event) {}
-
- @override
- void analysisOptionsUpdated(AnalysisDriver driver) {}
-
- @override
- void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
- var driver = driverMap[contextFolder.path];
- if (driver != null) {
- changeSet.addedFiles.forEach((source) {
- driver.addFile(source);
- });
- changeSet.changedFiles.forEach((source) {
- driver.changeFile(source);
- });
- changeSet.removedFiles.forEach((source) {
- driver.removeFile(source);
- });
- }
- }
-
- @override
- void applyFileRemoved(AnalysisDriver driver, String file) {
- driver.removeFile(file);
- }
-
- void assertContextFiles(String contextPath, List<String> expectedFiles) {
- expect(getCurrentFilePaths(contextPath), unorderedEquals(expectedFiles));
- }
-
- void assertContextPaths(List<String> expected) {
- expect(currentContextRoots, unorderedEquals(expected));
- }
-
- @override
- void broadcastWatchEvent(WatchEvent event) {
- watchEvents.add(event);
- }
-
- @override
- ContextBuilder createContextBuilder(Folder folder) {
- var builderOptions = ContextBuilderOptions();
- var builder = ContextBuilder(resourceProvider, sdkManager, null,
- options: builderOptions);
- return builder;
- }
-
- /// Return the paths to the files being analyzed in the current context root.
- Iterable<Source> currentFileSources(String contextPath) {
- if (currentDriver == null) {
- return <Source>[];
- }
- var driver = driverMap[contextPath];
- var sourceFactory = driver.sourceFactory;
- return driver.addedFiles.map((String path) {
- var file = resourceProvider.getFile(path);
- var source = file.createSource();
- var uri = sourceFactory.restoreUri(source);
- return file.createSource(uri);
- });
- }
-
- /// Return the paths to the files being analyzed in the current context root.
- Iterable<String> getCurrentFilePaths(String contextPath) {
- if (currentDriver == null) {
- return <String>[];
- }
- return driverMap[contextPath].addedFiles;
- }
-
- @override
- void removeContext(Folder folder, List<String> flushedFiles) {
- var path = folder.path;
- expect(currentContextRoots, contains(path));
- currentContextTimestamps.remove(path);
- currentContextFilePaths.remove(path);
- currentContextSources.remove(path);
- lastFlushedFiles = flushedFiles;
- }
-}
-
-class _MockSource implements Source {
- @override
- final String fullName;
-
- _MockSource(this.fullName);
-
- @override
- dynamic noSuchMethod(Invocation invocation) {
- throw StateError('Unexpected invocation of ${invocation.memberName}');
- }
-}
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index 06c2e37..94445ff 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -14,9 +14,11 @@
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -303,31 +305,157 @@
@reflectiveTest
class AnalysisDomainTest extends AbstractAnalysisTest {
- Map<String, List<AnalysisError>> filesErrors = {};
+ final Map<String, List<AnalysisError>> filesErrors = {};
+
+ /// The files for which `analysis.flushResults` was received.
+ final List<String> flushResults = [];
+
+ String get testFilePath => '$testPackageLibPath/test.dart';
+
+ String get testPackageLibPath => '$testPackageRootPath/lib';
+
+ String get testPackageRootPath => '$workspaceRootPath/test';
+
+ String get workspaceRootPath => '/home';
void assertHasErrors(String path) {
- expect(filesErrors[path], isNotEmpty);
+ path = convertPath(path);
+ expect(filesErrors[path], isNotEmpty, reason: path);
}
void assertNoErrors(String path) {
- expect(filesErrors[path], isEmpty);
+ path = convertPath(path);
+ expect(filesErrors[path], isEmpty, reason: path);
}
void assertNoErrorsNotification(String path) {
- expect(filesErrors[path], isNull);
+ path = convertPath(path);
+ expect(filesErrors[path], isNull, reason: path);
+ }
+
+ void forgetReceivedErrors() {
+ filesErrors.clear();
}
@override
void processNotification(Notification notification) {
+ if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
+ var decoded = AnalysisFlushResultsParams.fromNotification(notification);
+ flushResults.addAll(decoded.files);
+ }
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
var decoded = AnalysisErrorsParams.fromNotification(notification);
filesErrors[decoded.file] = decoded.errors;
}
}
- Future<void> test_fileSystem_addFile_excluded() async {
- var a_path = convertPath('$projectPath/lib/a.dart');
- var b_path = convertPath('$projectPath/lib/b.dart');
+ Future<void> test_fileSystem_addFile_analysisOptions() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ _createFilesWithErrors([a_path, b_path]);
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // Both a.dart and b.dart are analyzed.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, b_path],
+ notAnalyzed: [],
+ );
+
+ // Write the options file that excludes b.dart
+ newFile('$testPackageRootPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - lib/b.dart
+''');
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // Errors for all files were flushed, and only a.dart is reported again.
+ _assertFlushedResults([a_path, b_path]);
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [b_path],
+ );
+ }
+
+ Future<void> test_fileSystem_addFile_analysisOptions_analysis() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var options_path = '$testPackageRootPath/analysis_options.yaml';
+
+ newFile(a_path, content: 'error');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // a.dart was analyzed
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [options_path],
+ );
+
+ // Add a non-Dart file that we know how to analyze.
+ newFile(options_path, content: '''
+analyzer:
+ error:
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // Both files were analyzed.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, options_path],
+ notAnalyzed: [],
+ );
+ }
+
+ Future<void> test_fileSystem_addFile_androidManifestXml() async {
+ var path = '$testPackageRootPath/AndroidManifest.xml';
+
+ newFile('$testPackageLibPath/a.dart', content: '');
+
+ newFile('$testPackageRootPath/analysis_options.yaml', content: '''
+analyzer:
+ optional-checks:
+ chrome-os-manifest-checks: true
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ newFile(path, content: '<manifest/>');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // No touch-screen.
+ assertHasErrors(path);
+ }
+
+ Future<void> test_fileSystem_addFile_dart() async {
+ var a_path = '$testPackageLibPath/a.dart';
+
+ // We have to create the folder, otherwise there is nothing to watch.
+ newFolder(testPackageLibPath);
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We don't have a.dart yet.
+ assertNoErrorsNotification(a_path);
+
+ _createFilesWithErrors([a_path]);
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We created a.dart, so it should be analyzed.
+ assertHasErrors(a_path);
+ }
+
+ Future<void> test_fileSystem_addFile_dart_excluded() async {
+ var a_path = '$projectPath/lib/a.dart';
+ var b_path = '$projectPath/lib/b.dart';
newFile('$projectPath/analysis_options.yaml', content: r'''
analyzer:
@@ -360,11 +488,267 @@
assertNoErrors(b_path);
}
- Future<void> test_fileSystem_changeFile_excluded() async {
- var a_path = convertPath('$projectPath/lib/a.dart');
- var b_path = convertPath('$projectPath/lib/b.dart');
+ Future<void> test_fileSystem_addFile_dotPackagesFile() async {
+ var aaaLibPath = '/packages/aaa/lib';
+ var a_path = '$aaaLibPath/a.dart';
- newFile('$projectPath/analysis_options.yaml', content: r'''
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We cannot resolve `package:aaa/a.dart`
+ assertHasErrors(testFilePath);
+
+ // Write `.packages`, recreate analysis contexts.
+ newFile('$testPackageRootPath/.packages', content: '''
+aaa:${toUriStr(aaaLibPath)}
+''');
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_fileSystem_addFile_fixDataYaml() async {
+ var path = '$testPackageLibPath/fix_data.yaml';
+
+ newFile('$testPackageLibPath/a.dart', content: '');
+
+ // Make sure that it is a package.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder(),
+ );
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ // No `fix_data.yaml` to analyze yet.
+ assertNoErrorsNotification(path);
+
+ // Create it, will be analyzed.
+ newFile(path, content: '0: 1');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // And it has errors.
+ assertHasErrors(path);
+
+ // We don't recreate analysis contexts.
+ _assertFlushedResults([]);
+ }
+
+ Future<void> test_fileSystem_addFile_packageConfigJsonFile() async {
+ var aaaRootPath = '/packages/aaa';
+ var a_path = '$aaaRootPath/lib/a.dart';
+
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We cannot resolve `package:aaa/a.dart`
+ assertHasErrors(testFilePath);
+
+ // Write `package_config.json`, recreate analysis contexts.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
+ );
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_fileSystem_addFile_pubspec_analysis() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var pubspec_path = '$testPackageRootPath/pubspec.yaml';
+
+ newFile(a_path, content: 'error');
+
+ // Write an empty file to force a new analysis context.
+ // We look for `pubspec.yaml` files only in analysis context roots.
+ newFile('$testPackageRootPath/analysis_options.yaml', content: '');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // a.dart was analyzed
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [pubspec_path],
+ );
+
+ // Add a non-Dart file that we know how to analyze.
+ newFile(pubspec_path, content: '''
+name: sample
+dependencies: true
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // Both files were analyzed.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, pubspec_path],
+ notAnalyzed: [],
+ );
+ }
+
+ Future<void> test_fileSystem_addFile_unrelated() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var unrelated_path = '$testPackageRootPath/unrelated.txt';
+
+ newFile(a_path, content: 'error');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // a.dart was analyzed
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [unrelated_path],
+ );
+
+ // Add an unrelated file, no analysis.
+ newFile(unrelated_path, content: 'anything');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // No analysis.
+ _assertFlushedResults([]);
+ _assertAnalyzedFiles(hasErrors: [], notAnalyzed: [a_path]);
+ }
+
+ Future<void> test_fileSystem_changeFile_analysisOptions() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+ var c_path = '$testPackageLibPath/c.dart';
+
+ _createFilesWithErrors([a_path, b_path, c_path]);
+
+ // Exclude b.dart from analysis.
+ newFile('$testPackageRootPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - lib/b.dart
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // Only a.dart is analyzed, because b.dart is excluded.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, c_path],
+ notAnalyzed: [b_path],
+ );
+
+ // Exclude c.dart from analysis.
+ newFile('$testPackageRootPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - lib/c.dart
+''');
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // Errors for all files were flushed, a.dart and b.dart analyzed.
+ _assertFlushedResults([a_path, c_path]);
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, b_path],
+ notAnalyzed: [c_path],
+ );
+ }
+
+ Future<void> test_fileSystem_changeFile_androidManifestXml() async {
+ var path = '$testPackageRootPath/AndroidManifest.xml';
+
+ newFile('$testPackageLibPath/a.dart', content: '');
+
+ // Has an error - no touch screen.
+ newFile(path, content: '<manifest/>');
+
+ newFile('$testPackageRootPath/analysis_options.yaml', content: '''
+analyzer:
+ optional-checks:
+ chrome-os-manifest-checks: true
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ // Forget and check that we did.
+ forgetReceivedErrors();
+ assertNoErrorsNotification(path);
+
+ // Update the file, so analyze it.
+ newFile(path, content: '<manifest/>');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // An error was reports.
+ assertHasErrors(path);
+ }
+
+ Future<void> test_fileSystem_changeFile_dart() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ newFile(a_path, content: r'''
+class A2 {}
+''');
+
+ newFile(b_path, content: r'''
+import 'a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ assertNoErrors(a_path);
+ assertHasErrors(b_path);
+ forgetReceivedErrors();
+
+ // Update a.dart so that b.dart has no error.
+ newFile(a_path, content: 'class A {}');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // The update of a.dart fixed the error in b.dart
+ assertNoErrors(a_path);
+ assertNoErrors(b_path);
+ }
+
+ Future<void> test_fileSystem_changeFile_dart_excluded() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ newFile('$testPackageRootPath/analysis_options.yaml', content: r'''
analyzer:
exclude:
- "**/a.dart"
@@ -379,7 +763,7 @@
void f(A a) {}
''');
- createProject();
+ setRoots(included: [workspaceRootPath], excluded: []);
await pumpEventQueue();
await server.onAnalysisComplete;
@@ -399,11 +783,180 @@
assertNoErrors(b_path);
}
- Future<void> test_fileSystem_deleteFile_excluded() async {
- var a_path = convertPath('$projectPath/lib/a.dart');
- var b_path = convertPath('$projectPath/lib/b.dart');
+ Future<void> test_fileSystem_changeFile_dotPackagesFile() async {
+ var aaaLibPath = '/packages/aaa/lib';
+ var a_path = '$aaaLibPath/a.dart';
- newFile('$projectPath/analysis_options.yaml', content: r'''
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ // Write `.packages` empty, without `package:aaa`.
+ newFile('$testPackageRootPath/.packages', content: '');
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We cannot resolve `package:aaa/a.dart`
+ assertHasErrors(testFilePath);
+
+ // Write `.packages`, recreate analysis contexts.
+ newFile('$testPackageRootPath/.packages', content: '''
+aaa:${toUriStr(aaaLibPath)}
+''');
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_fileSystem_changeFile_fixDataYaml() async {
+ var path = '$testPackageLibPath/fix_data.yaml';
+
+ newFile('$testPackageLibPath/a.dart', content: '');
+
+ // Make sure that it is a package.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder(),
+ );
+
+ // This file has an error.
+ newFile(path, content: '0: 1');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ // The file was analyzed.
+ assertHasErrors(path);
+
+ // Replace with the context that does not have errors.
+ newFile(path, content: r'''
+version: 1
+transforms: []
+''');
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // And it has errors.
+ assertNoErrors(path);
+
+ // We don't recreate analysis contexts.
+ _assertFlushedResults([]);
+ }
+
+ Future<void> test_fileSystem_changeFile_packageConfigJsonFile() async {
+ var aaaRootPath = '/packages/aaa';
+ var a_path = '$aaaRootPath/lib/a.dart';
+
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ // Write the empty file, without `package:aaa`.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder(),
+ );
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We cannot resolve `package:aaa/a.dart`
+ assertHasErrors(testFilePath);
+
+ // Write `package_config.json`, recreate analysis contexts.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
+ );
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_fileSystem_deleteFile_analysisOptions() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ _createFilesWithErrors([a_path, b_path]);
+
+ // Exclude b.dart from analysis.
+ newFile('$testPackageRootPath/analysis_options.yaml', content: r'''
+analyzer:
+ exclude:
+ - lib/b.dart
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // Only a.dart is analyzed, because b.dart is excluded.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [b_path],
+ );
+
+ // Delete the options file.
+ deleteFile('$testPackageRootPath/analysis_options.yaml');
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // Errors for a.dart were flushed, a.dart and b.dart analyzed.
+ _assertFlushedResults([a_path]);
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, b_path],
+ notAnalyzed: [],
+ );
+ }
+
+ Future<void> test_fileSystem_deleteFile_dart() async {
+ var a_path = '$testPackageLibPath/a.dart';
+
+ _createFilesWithErrors([a_path]);
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // a.dart was analyzed
+ assertHasErrors(a_path);
+ forgetReceivedErrors();
+
+ deleteFile(a_path);
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We deleted a.dart, its errors should be flushed.
+ _assertFlushedResults([a_path]);
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_fileSystem_deleteFile_dart_excluded() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ newFile('$testPackageRootPath/analysis_options.yaml', content: r'''
analyzer:
exclude:
- "**/a.dart"
@@ -418,7 +971,7 @@
void f(A a) {}
''');
- createProject();
+ setRoots(included: [workspaceRootPath], excluded: []);
await pumpEventQueue();
await server.onAnalysisComplete;
@@ -436,27 +989,337 @@
assertHasErrors(b_path);
}
- Future<void> test_setRoots_packages() {
- // prepare package
- var pkgFile = newFile('/packages/pkgA/libA.dart', content: '''
-library lib_a;
+ Future<void> test_fileSystem_deleteFile_dotPackagesFile() async {
+ var aaaLibPath = '/packages/aaa/lib';
+ var a_path = '$aaaLibPath/a.dart';
+
+ newFile(a_path, content: '''
class A {}
-''').path;
- newFile('/project/.packages',
- content: 'pkgA:${toUriStr('/packages/pkgA')}');
- addTestFile('''
-import 'package:pkgA/libA.dart';
-void f(A a) {
-}
''');
+
+ newFile('$testPackageRootPath/.packages', content: '''
+aaa:${toUriStr(aaaLibPath)}
+''');
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // Write `.packages`, recreate analysis contexts.
+ deleteFile('$testPackageRootPath/.packages');
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We cannot resolve `package:aaa/a.dart`
+ assertHasErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_fileSystem_deleteFile_packageConfigJsonFile() async {
+ var aaaRootPath = '/packages/aaa';
+ var a_path = '$aaaRootPath/lib/a.dart';
+
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ // Write the empty file, without `package:aaa`.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
+ );
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // Delete `package_config.json`, recreate analysis contexts.
+ deleteFile(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ );
+
+ await pumpEventQueue();
+ await server.onAnalysisComplete;
+
+ // We cannot resolve 'package:aaa/a.dart', so errors.
+ assertHasErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_setRoots_dotPackagesFile() async {
+ var aaaLibPath = '/packages/aaa/lib';
+ var a_path = '$aaaLibPath/a.dart';
+
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ newFile('$testPackageRootPath/.packages', content: '''
+aaa:${toUriStr(aaaLibPath)}
+''');
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
// create project and wait for analysis
- createProject();
- return waitForTasksFinished().then((_) {
- // if 'package:pkgA/libA.dart' was resolved, then there are no errors
- expect(filesErrors[testFile], isEmpty);
- // errors are not reported for packages
- expect(filesErrors[pkgFile], isNull);
- });
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ Future<void> test_setRoots_includedFile() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ _createFilesWithErrors([a_path, b_path]);
+
+ setRoots(included: [a_path], excluded: []);
+
+ // Only a.dart is included, so b.dart is not analyzed.
+ await server.onAnalysisComplete;
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [b_path],
+ );
+ }
+
+ Future<void> test_setRoots_includedFile_setRoots() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ _createFilesWithErrors([a_path, b_path]);
+
+ // Include only single file.
+ setRoots(included: [a_path], excluded: []);
+ await server.onAnalysisComplete;
+
+ // So, only a.dart is analyzed, and b.dart is not.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [b_path],
+ );
+
+ // Include the folder that contains both a.dart and b.dart
+ setRoots(included: [testPackageRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // So, both a.dart and b.dart are analyzed.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path, b_path],
+ notAnalyzed: [],
+ );
+ }
+
+ Future<void> test_setRoots_includedFileFolder() async {
+ var includedFile = '$testPackageLibPath/a.dart';
+ var includedFolder = '$testPackageLibPath/foo';
+ var includedFolderFile1 = '$includedFolder/1.dart';
+ var includedFolderFile2 = '$includedFolder/2.dart';
+ var notIncludedFile = '$testPackageLibPath/b.dart';
+
+ _createFilesWithErrors([
+ includedFile,
+ includedFolderFile1,
+ includedFolderFile2,
+ notIncludedFile,
+ ]);
+
+ setRoots(included: [includedFile, includedFolder], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We can combine a file, and a folder as included paths.
+ // And the file that is not in there is not analyzed.
+ _assertAnalyzedFiles(hasErrors: [
+ includedFile,
+ includedFolderFile1,
+ includedFolderFile2,
+ ], notAnalyzed: [
+ notIncludedFile,
+ ]);
+ }
+
+ Future<void> test_setRoots_includedFolder_analysisOptions_exclude() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var b_path = '$testPackageLibPath/b.dart';
+
+ newOptionsFile(testPackageRootPath, content: '''
+analyzer:
+ exclude:
+ - "**/b.dart"
+''');
+
+ _createFilesWithErrors([a_path, b_path]);
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ // b.dart is excluded using the options file.
+ await server.onAnalysisComplete;
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [b_path],
+ );
+ }
+
+ @FailingTest(reason: 'Not implemented in ContextLocator')
+ Future<void> test_setRoots_includedFolder_excludedFile() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var excluded_path = '$testPackageRootPath/excluded/b.dart';
+
+ _createFilesWithErrors([a_path, excluded_path]);
+
+ setRoots(
+ included: [workspaceRootPath],
+ excluded: [excluded_path],
+ );
+ await server.onAnalysisComplete;
+
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [excluded_path],
+ );
+ }
+
+ Future<void> test_setRoots_includedFolder_excludedFolder() async {
+ var a_path = '$testPackageLibPath/a.dart';
+ var excluded_path = '$testPackageRootPath/excluded/b.dart';
+
+ _createFilesWithErrors([a_path, excluded_path]);
+
+ setRoots(
+ included: [workspaceRootPath],
+ excluded: ['$testPackageRootPath/excluded'],
+ );
+ await server.onAnalysisComplete;
+
+ // a.dart is analyzed, but b.dart is in the excluded folder.
+ _assertAnalyzedFiles(
+ hasErrors: [a_path],
+ notAnalyzed: [excluded_path],
+ );
+ }
+
+ Future<void> test_setRoots_notDartFile_androidManifestXml() async {
+ var path = '$testPackageRootPath/AndroidManifest.xml';
+
+ newFile('$testPackageLibPath/a.dart', content: '');
+
+ newFile('$testPackageRootPath/analysis_options.yaml', content: '''
+analyzer:
+ optional-checks:
+ chrome-os-manifest-checks: true
+''');
+
+ newFile(path, content: '<manifest/>');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ // No touch-screen.
+ assertHasErrors(path);
+ }
+
+ Future<void> test_setRoots_notDartFile_fixDataYaml() async {
+ var path = '$testPackageLibPath/fix_data.yaml';
+
+ // Make sure that it is a package.
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder(),
+ );
+
+ // So, `lib/fix_data.yaml` will be analyzed.
+ newFile(path, content: '0: 1');
+
+ setRoots(included: [workspaceRootPath], excluded: []);
+
+ assertHasErrors(path);
+ }
+
+ Future<void> test_setRoots_packageConfigJsonFile() async {
+ var aaaRootPath = '/packages/aaa';
+ var a_path = '$aaaRootPath/lib/a.dart';
+
+ newFile(a_path, content: '''
+class A {}
+''');
+
+ writePackageConfig(
+ '$testPackageRootPath/.dart_tool/package_config.json',
+ PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
+ );
+
+ newFile(testFilePath, content: '''
+import 'package:aaa/a.dart';
+void f(A a) {}
+''');
+
+ // create project and wait for analysis
+ setRoots(included: [workspaceRootPath], excluded: []);
+ await server.onAnalysisComplete;
+
+ // We have `A` in 'package:aaa/a.dart', so no errors.
+ assertNoErrors(testFilePath);
+
+ // errors are not reported for packages
+ assertNoErrorsNotification(a_path);
+ }
+
+ void writePackageConfig(String path, PackageConfigFileBuilder config) {
+ newFile(path, content: config.toContent(toUriStr: toUriStr));
+ }
+
+ void _assertAnalyzedFiles({
+ @required List<String> hasErrors,
+ @required List<String> notAnalyzed,
+ }) {
+ for (var path in hasErrors) {
+ assertHasErrors(path);
+ }
+
+ for (var path in notAnalyzed) {
+ assertNoErrorsNotification(path);
+ }
+
+ filesErrors.clear();
+ }
+
+ void _assertFlushedResults(List<String> paths) {
+ var convertedPaths = paths.map(convertPath).toList();
+ expect(flushResults, convertedPaths);
+ flushResults.clear();
+ }
+
+ /// Create files with a content that has a compile time error.
+ /// So, when analyzed, these files will satisfy [assertHasErrors].
+ void _createFilesWithErrors(List<String> paths) {
+ for (var path in paths) {
+ newFile(path, content: 'error');
+ }
}
}
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index ec7ad30..7173df2 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -266,7 +266,7 @@
assertValidId(result2.id);
// Wait for all processing to be complete
- await analysisHandler.server.analysisDriverScheduler.waitForIdle();
+ await analysisHandler.server.onAnalysisComplete;
await pumpEventQueue();
// Assert that first request has been aborted
@@ -304,7 +304,7 @@
assertValidId(completionId);
// Wait for all processing to be complete
- await analysisHandler.server.analysisDriverScheduler.waitForIdle();
+ await analysisHandler.server.onAnalysisComplete;
await pumpEventQueue();
// Assert that request has been aborted
diff --git a/pkg/analysis_server/test/domain_execution_test.dart b/pkg/analysis_server/test/domain_execution_test.dart
index 72b7b5b..b7b387c 100644
--- a/pkg/analysis_server/test/domain_execution_test.dart
+++ b/pkg/analysis_server/test/domain_execution_test.dart
@@ -220,9 +220,7 @@
}
void test_mapUri_file_dartUriKind() {
- var path = server.findSdk().mapDartUri('dart:async').fullName;
- // hack - pretend that the SDK file exists in the project FS
- newFile(path, content: '// hack');
+ var path = _mapUri(uri: 'dart:async').file;
// map file
var result = _mapUri(file: path);
expect(result.file, isNull);
diff --git a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
index 6d9e3fc..fe4524b 100644
--- a/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
+++ b/pkg/analysis_server/test/src/domains/completion/get_suggestions_available_test.dart
@@ -79,7 +79,10 @@
// Force the server to rebuild all contexts, as happens when the file watcher
// fails on Windows.
// https://github.com/dart-lang/sdk/issues/44650
- server.contextManager.refresh(null);
+ server.contextManager.refresh();
+
+ // Give it time to process the newly scheduled files.
+ await pumpEventQueue(times: 5000);
// Ensure the set is still returned after the rebuild.
results = await _getSuggestions(testFile, 0);
diff --git a/pkg/analysis_server/test/test_all.dart b/pkg/analysis_server/test/test_all.dart
index 64fb2ea..eca22e0 100644
--- a/pkg/analysis_server/test/test_all.dart
+++ b/pkg/analysis_server/test/test_all.dart
@@ -11,7 +11,6 @@
import 'channel/test_all.dart' as channel;
import 'client/test_all.dart' as client;
import 'completion_test.dart' as completion;
-import 'context_manager_test.dart' as context_manager;
import 'domain_analysis_test.dart' as domain_analysis;
import 'domain_completion_test.dart' as domain_completion;
import 'domain_diagnostic_test.dart' as domain_experimental;
@@ -39,7 +38,6 @@
channel.main();
client.main();
completion.main();
- context_manager.main();
domain_analysis.main();
domain_completion.main();
domain_edit_dartfix.main();
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 1ece128..f64e776 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -6,6 +6,8 @@
* Added `Folder.isRoot`.
* Added `FolderExtension` with `withAncestors`.
* Added `ContextRoot.workspace`, deprecated `AnalysisContext.workspace`.
+* Deprecated `ElementVisitor.visitFunctionTypeAliasElement()`.
+ Override `ElementVisitor.visitTypeAliasElement()` instead.
## 1.0.0
* Stable null safety release.
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 336473b..91e2155 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -995,6 +995,7 @@
R? visitFunctionElement(FunctionElement element);
+ @Deprecated('Override visitTypeAliasElement() instead')
R? visitFunctionTypeAliasElement(FunctionTypeAliasElement element);
R? visitGenericFunctionTypeElement(GenericFunctionTypeElement element);
diff --git a/pkg/analyzer/lib/dart/element/visitor.dart b/pkg/analyzer/lib/dart/element/visitor.dart
index 27f3283..7da9bd6 100644
--- a/pkg/analyzer/lib/dart/element/visitor.dart
+++ b/pkg/analyzer/lib/dart/element/visitor.dart
@@ -118,6 +118,7 @@
R? visitFunctionElement(FunctionElement element) =>
visitLocalElement(element);
+ @Deprecated('Override visitTypeAliasElement() instead')
@override
R? visitFunctionTypeAliasElement(FunctionTypeAliasElement element) =>
visitElement(element);
@@ -249,6 +250,7 @@
return null;
}
+ @Deprecated('Override visitTypeAliasElement() instead')
@override
R? visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
element.visitChildren(this);
@@ -369,6 +371,7 @@
@override
R? visitFunctionElement(FunctionElement element) => null;
+ @Deprecated('Override visitTypeAliasElement() instead')
@override
R? visitFunctionTypeAliasElement(FunctionTypeAliasElement element) => null;
@@ -450,6 +453,7 @@
@override
R? visitFunctionElement(FunctionElement element) => _throw(element);
+ @Deprecated('Override visitTypeAliasElement() instead')
@override
R? visitFunctionTypeAliasElement(FunctionTypeAliasElement element) =>
_throw(element);
diff --git a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
index cdd784b..30d1728 100644
--- a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
@@ -10,6 +10,8 @@
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/context_builder.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:cli_util/cli_util.dart';
/// An implementation of [AnalysisContextCollection].
@@ -25,12 +27,15 @@
AnalysisContextCollectionImpl({
ByteStore? byteStore,
Map<String, String>? declaredVariables,
+ bool drainStreams = true,
bool enableIndex = false,
required List<String> includedPaths,
List<String>? excludedPaths,
+ PerformanceLog? performanceLog,
ResourceProvider? resourceProvider,
bool retainDataForTesting = false,
String? sdkPath,
+ AnalysisDriverScheduler? scheduler,
}) : resourceProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE {
sdkPath ??= getSdkPath();
@@ -53,9 +58,12 @@
byteStore: byteStore,
contextRoot: root,
declaredVariables: DeclaredVariables.fromMap(declaredVariables ?? {}),
+ drainStreams: drainStreams,
enableIndex: enableIndex,
+ performanceLog: performanceLog,
retainDataForTesting: retainDataForTesting,
sdkPath: sdkPath,
+ scheduler: scheduler,
);
contexts.add(context);
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
index 1af6e4f..e98ab16 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
@@ -41,11 +41,12 @@
{ByteStore? byteStore,
required ContextRoot contextRoot,
DeclaredVariables? declaredVariables,
+ bool drainStreams = true,
bool enableIndex = false,
List<String>? librarySummaryPaths,
- @deprecated PerformanceLog? performanceLog,
+ PerformanceLog? performanceLog,
bool retainDataForTesting = false,
- @deprecated AnalysisDriverScheduler? scheduler,
+ AnalysisDriverScheduler? scheduler,
String? sdkPath,
String? sdkSummaryPath}) {
// TODO(scheglov) Remove this, and make `sdkPath` required.
@@ -95,8 +96,10 @@
// AnalysisDriver reports results into streams.
// We need to drain these streams to avoid memory leak.
- driver.results.drain();
- driver.exceptions.drain();
+ if (drainStreams) {
+ driver.results.drain();
+ driver.exceptions.drain();
+ }
DriverBasedAnalysisContext context =
DriverBasedAnalysisContext(resourceProvider, contextRoot, driver);
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index f8ef12c..c8b5796 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -4331,7 +4331,9 @@
@override
T? accept<T>(ElementVisitor<T> visitor) {
- return visitor.visitFunctionTypeAliasElement(this);
+ // ignore: deprecated_member_use_from_same_package
+ visitor.visitFunctionTypeAliasElement(this);
+ return visitor.visitTypeAliasElement(this);
}
@override
diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
index f218476..387ca4a 100644
--- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
@@ -581,14 +581,15 @@
// However in summary code it doesn't look like the AST node with span is
// available.
String prefix;
+ var genericClass = this.genericClass;
if (genericClass != null &&
- (genericClass!.name == "List" || genericClass!.name == "Map") &&
- genericClass!.library.isDartCore == true) {
+ (genericClass.name == "List" || genericClass.name == "Map") &&
+ genericClass.library.isDartCore == true) {
// This will become:
// "List element"
// "Map key"
// "Map value"
- prefix = "${genericClass!.name} $parameterName";
+ prefix = "${genericClass.name} $parameterName";
} else {
prefix = "Parameter '$parameterName'";
}
diff --git a/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart b/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart
index 2bcb023..85f2e08 100644
--- a/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart
+++ b/pkg/analyzer/lib/src/dartdoc/dartdoc_directive_info.dart
@@ -44,7 +44,9 @@
/// Process the given Dartdoc [comment], extracting the template directive if
/// there is one.
- void extractTemplate(String comment) {
+ void extractTemplate(String? comment) {
+ if (comment == null) return;
+
for (Match match in templateRegExp.allMatches(comment)) {
String name = match.group(1)!.trim();
String body = match.group(2)!.trim();
diff --git a/pkg/analyzer/lib/src/services/available_declarations.dart b/pkg/analyzer/lib/src/services/available_declarations.dart
index c5ca546..06acfc4 100644
--- a/pkg/analyzer/lib/src/services/available_declarations.dart
+++ b/pkg/analyzer/lib/src/services/available_declarations.dart
@@ -196,9 +196,10 @@
_addLibrariesWithPaths(sdkLibraries, _sdkLibraryPathList);
var dependencyLibraries = <Library>[];
- for (var pathPrefix in _pathPrefixToDependencyPathList.keys) {
+ for (var entry in _pathPrefixToDependencyPathList.entries) {
+ var pathPrefix = entry.key;
if (path.startsWith(pathPrefix)) {
- var pathList = _pathPrefixToDependencyPathList[pathPrefix]!;
+ var pathList = entry.value;
_addLibrariesWithPaths(dependencyLibraries, pathList);
break;
}
@@ -1493,13 +1494,13 @@
relevanceTags: ['ElementKind.FIELD'],
returnType: _getTypeAnnotationString(classMember.returnType),
);
- } else if (classMember.isSetter) {
+ } else if (classMember.isSetter && parameters != null) {
addDeclaration(
isDeprecated: isDeprecated,
isStatic: isStatic,
kind: DeclarationKind.SETTER,
name: classMember.name,
- parameters: parameters!.toSource(),
+ parameters: parameters.toSource(),
parameterNames: _getFormalParameterNames(parameters),
parameterTypes: _getFormalParameterTypes(parameters),
parent: parent,
@@ -1507,8 +1508,8 @@
requiredParameterCount:
_getFormalParameterRequiredCount(parameters),
);
- } else {
- var defaultArguments = _computeDefaultArguments(parameters!);
+ } else if (parameters != null) {
+ var defaultArguments = _computeDefaultArguments(parameters);
addDeclaration(
defaultArgumentListString: defaultArguments?.text,
defaultArgumentListTextRanges: defaultArguments?.ranges,
@@ -1605,11 +1606,12 @@
);
}
} else if (node is ExtensionDeclaration) {
- if (node.name != null) {
+ var name = node.name;
+ if (name != null) {
addDeclaration(
isDeprecated: isDeprecated,
kind: DeclarationKind.EXTENSION,
- name: node.name!,
+ name: name,
relevanceTags: ['ElementKind.EXTENSION'],
);
}
@@ -1626,20 +1628,20 @@
relevanceTags: ['ElementKind.FUNCTION'],
returnType: _getTypeAnnotationString(node.returnType),
);
- } else if (node.isSetter) {
+ } else if (node.isSetter && parameters != null) {
addDeclaration(
isDeprecated: isDeprecated,
kind: DeclarationKind.SETTER,
name: node.name,
- parameters: parameters!.toSource(),
+ parameters: parameters.toSource(),
parameterNames: _getFormalParameterNames(parameters),
parameterTypes: _getFormalParameterTypes(parameters),
relevanceTags: ['ElementKind.FUNCTION'],
requiredParameterCount:
_getFormalParameterRequiredCount(parameters),
);
- } else {
- var defaultArguments = _computeDefaultArguments(parameters!);
+ } else if (parameters != null) {
+ var defaultArguments = _computeDefaultArguments(parameters);
addDeclaration(
defaultArgumentListString: defaultArguments?.text,
defaultArgumentListTextRanges: defaultArguments?.ranges,
@@ -1731,35 +1733,27 @@
DartdocDirectiveInfo info = DartdocDirectiveInfo();
for (Directive directive in unit.directives) {
var comment = directive.documentationComment;
- if (comment != null) {
- info.extractTemplate(getCommentNodeRawText(comment)!);
- }
+ info.extractTemplate(getCommentNodeRawText(comment));
}
for (CompilationUnitMember declaration in unit.declarations) {
var comment = declaration.documentationComment;
- if (comment != null) {
- info.extractTemplate(getCommentNodeRawText(comment)!);
- }
+ info.extractTemplate(getCommentNodeRawText(comment));
if (declaration is ClassOrMixinDeclaration) {
for (ClassMember member in declaration.members) {
var comment = member.documentationComment;
- if (comment != null) {
- info.extractTemplate(getCommentNodeRawText(comment)!);
- }
+ info.extractTemplate(getCommentNodeRawText(comment));
}
} else if (declaration is EnumDeclaration) {
for (EnumConstantDeclaration constant in declaration.constants) {
var comment = constant.documentationComment;
- if (comment != null) {
- info.extractTemplate(getCommentNodeRawText(comment)!);
- }
+ info.extractTemplate(getCommentNodeRawText(comment));
}
}
}
Map<String, String> templateMap = info.templateMap;
- for (String name in templateMap.keys) {
- templateNames.add(name);
- templateValues.add(templateMap[name]!);
+ for (var entry in templateMap.entries) {
+ templateNames.add(entry.key);
+ templateValues.add(entry.value);
}
}
diff --git a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
index 3c6f084..18d7617 100644
--- a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
@@ -1050,17 +1050,18 @@
void _token(Token? token) {
if (token == null) return;
- if (_last != null) {
- if (_last!.next != token) {
+ var last = _last;
+ if (last != null) {
+ if (last.next != token) {
throw StateError(
- '|$_last| must be followed by |${_last!.next}|, got |$token|',
+ '|$last| must be followed by |${last.next}|, got |$token|',
);
}
}
// Print preceding comments as a separate sequence of tokens.
if (token.precedingComments != null) {
- var lastToken = _last;
+ var lastToken = last;
_last = null;
for (Token? c = token.precedingComments; c != null; c = c.next) {
_token(c);
diff --git a/pkg/analyzer/test/src/dart/analysis/dependency/base.dart b/pkg/analyzer/test/src/dart/analysis/dependency/base.dart
index 4d09fb6..3eec88f 100644
--- a/pkg/analyzer/test/src/dart/analysis/dependency/base.dart
+++ b/pkg/analyzer/test/src/dart/analysis/dependency/base.dart
@@ -9,10 +9,7 @@
import '../../resolution/context_collection_resolution.dart';
-class BaseDependencyTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
- // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
- // test cases.
+class BaseDependencyTest extends PubPackageResolutionTest {
late final String a;
late final String b;
late final String c;
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
index a11c045..f3bb485 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
@@ -36,17 +36,15 @@
/// Integration tests for resolution.
@reflectiveTest
class AnalysisDriverResolutionTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin, ElementsTypesMixin {
- // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
- // test cases.
+ with ElementsTypesMixin {
void assertDeclaredVariableType(SimpleIdentifier node, String expected) {
var element = node.staticElement as VariableElement;
assertType(element.type, expected);
}
- void assertDeclaredVariableTypeDynamic(SimpleIdentifier node) {
+ void assertDeclaredVariableTypeObject(SimpleIdentifier node) {
var element = node.staticElement as VariableElement;
- expect(element.type, isDynamicType);
+ expect(element.type, typeProvider.objectType);
}
/// Test that [argumentList] has exactly two type items `int` and `double`.
@@ -669,11 +667,11 @@
expect(atD.name.staticElement, elementD);
expect(atD.element, constructorD);
- expect(constC.staticType, interfaceTypeStar(elementC));
+ expect(constC.staticType, interfaceTypeNone(elementC));
var constructorName = constC.constructorName;
expect(constructorName.staticElement, constructorC);
- expect(constructorName.type.type, interfaceTypeStar(elementC));
+ expect(constructorName.type.type, interfaceTypeNone(elementC));
}
test_annotation_unprefixed_topLevelVariable() async {
@@ -978,7 +976,7 @@
var forTarget = forInvocation.target as SimpleIdentifier;
expect(forTarget.staticElement, itemsElement);
- var closureTypeStr = 'Null Function(int)';
+ var closureTypeStr = 'void Function(int)';
var closure =
forInvocation.argumentList.arguments[0] as FunctionExpression;
@@ -988,7 +986,7 @@
ParameterElement itemElement = closureElement.parameters[0];
itemElement1 = itemElement;
- expect(closureElement.returnType, typeProvider.nullType);
+ expect(closureElement.returnType, typeProvider.voidType);
assertType(closureElement.type, closureTypeStr);
expect(closure.staticType, same(closureElement.type));
@@ -1019,7 +1017,7 @@
var forTarget = forInvocation.target as SimpleIdentifier;
expect(forTarget.staticElement, itemsElement);
- var closureTypeStr = 'Null Function(int)';
+ var closureTypeStr = 'void Function(int)';
var closure =
forInvocation.argumentList.arguments[0] as FunctionExpression;
@@ -1029,7 +1027,7 @@
ParameterElement itemElement = closureElement.parameters[0];
expect(itemElement, isNot(same(itemElement1)));
- expect(closureElement.returnType, typeProvider.nullType);
+ expect(closureElement.returnType, typeProvider.voidType);
assertType(closureElement.type, closureTypeStr);
expect(closure.staticType, same(closureElement.type));
@@ -1330,7 +1328,7 @@
expect(constructorName.staticElement, same(aUnnamed));
TypeName typeName = constructorName.type;
- expect(typeName.type, interfaceTypeStar(aElement));
+ expect(typeName.type, interfaceTypeNone(aElement));
var identifier = typeName.name as SimpleIdentifier;
expect(identifier.staticElement, same(aElement));
@@ -1350,7 +1348,7 @@
expect(constructorName.staticElement, same(aNamed));
var typeName = constructorName.type;
- expect(typeName.type, interfaceTypeStar(aElement));
+ expect(typeName.type, interfaceTypeNone(aElement));
var identifier = typeName.name as SimpleIdentifier;
expect(identifier.staticElement, same(aElement));
@@ -1380,10 +1378,10 @@
var bNode = result.unit!.declarations[1] as ClassDeclaration;
TypeParameterType uType =
- typeParameterTypeStar(bNode.declaredElement!.typeParameters[0]);
+ typeParameterTypeNone(bNode.declaredElement!.typeParameters[0]);
InterfaceType auType = aElement.instantiate(
typeArguments: [uType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
{
@@ -1767,7 +1765,7 @@
VariableDeclaration fNode = fDeclaration.fields.variables[0];
var fElement = fNode.declaredElement as FieldElement;
expect(
- fElement.type, typeProvider.listType(typeParameterTypeStar(tElement)));
+ fElement.type, typeProvider.listType(typeParameterTypeNone(tElement)));
}
test_field_generic() async {
@@ -2091,7 +2089,7 @@
var aDeclaration = unit.declarations[1] as TopLevelVariableDeclaration;
VariableDeclaration aNode = aDeclaration.variables.variables[0];
var value = aNode.initializer as InstanceCreationExpression;
- expect(value.staticType, interfaceTypeStar(cElement));
+ expect(value.staticType, interfaceTypeNone(cElement));
var constructorName = value.constructorName;
expect(constructorName.name, isNull);
@@ -2109,7 +2107,7 @@
var bDeclaration = unit.declarations[2] as TopLevelVariableDeclaration;
VariableDeclaration bNode = bDeclaration.variables.variables[0];
var value = bNode.initializer as InstanceCreationExpression;
- expect(value.staticType, interfaceTypeStar(cElement));
+ expect(value.staticType, interfaceTypeNone(cElement));
var constructorName = value.constructorName;
expect(constructorName.staticElement, namedConstructor);
@@ -2144,7 +2142,7 @@
var creation = vNode.initializer as InstanceCreationExpression;
List<Expression> arguments = creation.argumentList.arguments;
- expect(creation.staticType, interfaceTypeStar(xElement));
+ expect(creation.staticType, interfaceTypeNone(xElement));
var constructorName = creation.constructorName;
expect(constructorName.name, isNull);
@@ -2185,7 +2183,7 @@
var aDeclaration = unit.declarations[1] as TopLevelVariableDeclaration;
VariableDeclaration aNode = aDeclaration.variables.variables[0];
var value = aNode.initializer as InstanceCreationExpression;
- expect(value.staticType, interfaceTypeStar(cElement));
+ expect(value.staticType, interfaceTypeNone(cElement));
var constructorName = value.constructorName;
expect(constructorName.name, isNull);
@@ -2206,7 +2204,7 @@
var bDeclaration = unit.declarations[2] as TopLevelVariableDeclaration;
VariableDeclaration bNode = bDeclaration.variables.variables[0];
var value = bNode.initializer as InstanceCreationExpression;
- expect(value.staticType, interfaceTypeStar(cElement));
+ expect(value.staticType, interfaceTypeNone(cElement));
var constructorName = value.constructorName;
expect(constructorName.staticElement, namedConstructor);
@@ -2254,7 +2252,7 @@
{
var cTypeInt = cElement.instantiate(
typeArguments: [typeProvider.intType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[0] as ExpressionStatement;
@@ -2283,7 +2281,7 @@
{
var cTypeDouble = cElement.instantiate(
typeArguments: [typeProvider.doubleType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[1] as ExpressionStatement;
@@ -2313,7 +2311,7 @@
{
var cTypeBool = cElement.instantiate(
typeArguments: [typeProvider.boolType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[2] as ExpressionStatement;
@@ -2369,7 +2367,7 @@
{
var cTypeInt = cElement.instantiate(
typeArguments: [typeProvider.intType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[0] as ExpressionStatement;
@@ -2391,7 +2389,7 @@
{
var cTypeBool = cElement.instantiate(
typeArguments: [typeProvider.boolType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[1] as ExpressionStatement;
@@ -2415,7 +2413,7 @@
{
var cTypeDouble = cElement.instantiate(
typeArguments: [typeProvider.doubleType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[2] as ExpressionStatement;
@@ -2438,7 +2436,7 @@
{
var cTypeBool = cElement.instantiate(
typeArguments: [typeProvider.boolType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
var statement = statements[3] as ExpressionStatement;
@@ -2555,7 +2553,7 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- assertDeclaredVariableTypeDynamic(findNode.simple('x,'));
+ assertDeclaredVariableTypeObject(findNode.simple('x,'));
assertDeclaredVariableType(findNode.simple('y,'), 'StackTrace');
}
@@ -2578,7 +2576,7 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- assertDeclaredVariableTypeDynamic(findNode.simple('e,'));
+ assertDeclaredVariableTypeObject(findNode.simple('e,'));
assertDeclaredVariableType(findNode.simple('s})'), 'StackTrace');
}
@@ -2591,7 +2589,7 @@
await resolveTestFile();
expect(result.errors, isNotEmpty);
- assertDeclaredVariableTypeDynamic(findNode.simple('e,'));
+ assertDeclaredVariableTypeObject(findNode.simple('e,'));
assertDeclaredVariableType(findNode.simple('s])'), 'StackTrace');
}
@@ -2686,7 +2684,7 @@
var invocation = findNode.functionExpressionInvocation('id(');
assertElement(invocation.function, findElement.topGet('id'));
- assertInvokeType(invocation, 'bool Function(Object, Object)');
+ assertInvokeType(invocation, 'bool Function(Object?, Object?)');
assertType(invocation, 'bool');
var aRef = invocation.argumentList.arguments[0];
@@ -4369,26 +4367,24 @@
test_local_parameter() async {
await assertNoErrorsInCode(r'''
-void main(int p) {
+void main(List<String> p) {
p;
}
''');
- InterfaceType intType = typeProvider.intType;
-
var main = result.unit!.declarations[0] as FunctionDeclaration;
List<Statement> statements = _getMainStatements(result);
// (int p)
VariableElement pElement = main.declaredElement!.parameters[0];
- expect(pElement.type, intType);
+ expect(pElement.type, listNone(typeProvider.stringType));
// p;
{
var statement = statements[0] as ExpressionStatement;
var identifier = statement.expression as SimpleIdentifier;
expect(identifier.staticElement, pElement);
- expect(identifier.staticType, intType);
+ expect(identifier.staticType, listNone(typeProvider.stringType));
}
}
@@ -5210,7 +5206,7 @@
test_methodInvocation_explicitCall_functionTarget() async {
addTestFile(r'''
-main(double computation(int p)) {
+f(double computation(int p)) {
computation.call(1);
}
''');
@@ -5933,7 +5929,7 @@
test_outline_type_genericFunction() async {
addTestFile(r'''
-int Function(double) g() => null;
+int Function(double) g() => (double g) => 0;
''');
await resolveTestFile();
expect(result.errors, isEmpty);
@@ -5950,8 +5946,8 @@
test_outline_type_topLevelVar_named() async {
addTestFile(r'''
-int a;
-List<double> b;
+int a = 0;
+List<double> b = [];
''');
await resolveTestFile();
expect(result.errors, isEmpty);
@@ -6176,7 +6172,7 @@
SimpleIdentifier prefix = prefixed.prefix;
expect(prefix.staticElement, same(vElement));
- expect(prefix.staticType, interfaceTypeStar(cElement));
+ expect(prefix.staticType, interfaceTypeNone(cElement));
SimpleIdentifier identifier = prefixed.identifier;
expect(identifier.staticElement, same(fElement.getter));
@@ -6216,7 +6212,7 @@
test_prefixedIdentifier_explicitCall() async {
addTestFile(r'''
-main(double computation(int p)) {
+f(double computation(int p)) {
computation.call;
}
''');
@@ -6519,7 +6515,7 @@
newC.constructorName.staticElement,
cClassElement.unnamedConstructor,
);
- expect(newC.staticType, interfaceTypeStar(cClassElement));
+ expect(newC.staticType, interfaceTypeNone(cClassElement));
expect(access.propertyName.staticElement, same(fElement.getter));
expect(access.propertyName.staticType, typeProvider.intType);
@@ -6556,7 +6552,7 @@
newC.constructorName.staticElement,
cClassElement.unnamedConstructor,
);
- expect(newC.staticType, interfaceTypeStar(cClassElement));
+ expect(newC.staticType, interfaceTypeNone(cClassElement));
expect(access.propertyName.staticElement, same(fElement.getter));
expect(access.propertyName.staticType, typeProvider.intType);
@@ -6720,7 +6716,7 @@
var target = invocation.target as SuperExpression;
expect(
- target.staticType, interfaceTypeStar(bNode.declaredElement!)); // raw
+ target.staticType, interfaceTypeNone(bNode.declaredElement!)); // raw
expect(invocation.methodName.staticElement, same(methodElement));
}
@@ -6742,7 +6738,7 @@
var target = propertyAccess.target as SuperExpression;
expect(
- target.staticType, interfaceTypeStar(bNode.declaredElement!)); // raw
+ target.staticType, interfaceTypeNone(bNode.declaredElement!)); // raw
expect(propertyAccess.propertyName.staticElement, same(getterElement));
expect(propertyAccess.propertyName.staticType, typeProvider.intType);
@@ -6772,7 +6768,7 @@
var target = propertyAccess.target as SuperExpression;
expect(
- target.staticType, interfaceTypeStar(bNode.declaredElement!)); // raw
+ target.staticType, interfaceTypeNone(bNode.declaredElement!)); // raw
if (hasAssignmentLeftResolution) {
expect(propertyAccess.propertyName.staticElement, same(setterElement));
@@ -6790,7 +6786,7 @@
var target = binary.leftOperand as ThisExpression;
expect(
- target.staticType, interfaceTypeStar(bNode.declaredElement!)); // raw
+ target.staticType, interfaceTypeNone(bNode.declaredElement!)); // raw
expect(binary.staticElement, same(operatorElement));
expect(binary.staticType, typeProvider.intType);
@@ -6832,7 +6828,7 @@
List<Statement> testStatements = testBody.block.statements;
var elementA = findElement.class_('A');
- var thisTypeA = interfaceTypeStar(elementA);
+ var thisTypeA = interfaceTypeNone(elementA);
// method(1);
{
@@ -6998,7 +6994,7 @@
{
var expectedType = aElement.instantiate(
typeArguments: [typeProvider.boolType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeName superClass = dNode.extendsClause!.superclass;
@@ -7012,7 +7008,7 @@
{
var expectedType = bElement.instantiate(
typeArguments: [typeProvider.intType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeName mixinType = dNode.withClause!.mixinTypes[0];
@@ -7026,7 +7022,7 @@
{
var expectedType = cElement.instantiate(
typeArguments: [typeProvider.doubleType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeName implementedType = dNode.implementsClause!.interfaces[0];
@@ -7067,7 +7063,7 @@
{
var expectedType = aElement.instantiate(
typeArguments: [typeProvider.boolType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeName superClass = dNode.superclass;
@@ -7081,7 +7077,7 @@
{
var expectedType = bElement.instantiate(
typeArguments: [typeProvider.intType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeName mixinType = dNode.withClause.mixinTypes[0];
@@ -7095,7 +7091,7 @@
{
var expectedType = cElement.instantiate(
typeArguments: [typeProvider.doubleType],
- nullabilitySuffix: NullabilitySuffix.star,
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeName interfaceType = dNode.implementsClause!.interfaces[0];
@@ -7407,7 +7403,7 @@
VariableDeclaration bNode = bDeclaration.fields.variables[0];
expect(bNode.declaredElement, same(bElement));
- expect(bElement.type, typeParameterTypeStar(tElement));
+ expect(bElement.type, typeParameterTypeNone(tElement));
expect(bNode.name.staticElement, same(bElement));
expect(bNode.name.staticType, isNull);
}
@@ -7647,7 +7643,7 @@
name: 'b',
offset: 27,
kind: ParameterKind.REQUIRED,
- type: typeParameterTypeStar(aliasElement.typeParameters[0]));
+ type: typeParameterTypeNone(aliasElement.typeParameters[0]));
}
test_top_typeParameter() async {
@@ -7673,7 +7669,7 @@
expect(tNode.declaredElement, same(cElement.typeParameters[0]));
var bound = tNode.bound as TypeName;
- expect(bound.type, interfaceTypeStar(aElement));
+ expect(bound.type, interfaceTypeNone(aElement));
var boundIdentifier = bound.name as SimpleIdentifier;
expect(boundIdentifier.staticElement, same(aElement));
@@ -7683,8 +7679,8 @@
{
var listElement = typeProvider.listElement;
var listOfA = listElement.instantiate(
- typeArguments: [interfaceTypeStar(aElement)],
- nullabilitySuffix: NullabilitySuffix.star,
+ typeArguments: [interfaceTypeNone(aElement)],
+ nullabilitySuffix: NullabilitySuffix.none,
);
TypeParameter uNode = cNode.typeParameters!.typeParameters[1];
@@ -7698,7 +7694,7 @@
expect(listIdentifier.staticType, isNull);
var aTypeName = bound.typeArguments!.arguments[0] as TypeName;
- expect(aTypeName.type, interfaceTypeStar(aElement));
+ expect(aTypeName.type, interfaceTypeNone(aElement));
var aIdentifier = aTypeName.name as SimpleIdentifier;
expect(aIdentifier.staticElement, same(aElement));
@@ -7745,7 +7741,7 @@
var exceptionNode = catchClause.exceptionParameter as SimpleIdentifier;
var exceptionElement =
exceptionNode.staticElement as LocalVariableElement;
- expect(exceptionElement.type, DynamicTypeImpl.instance);
+ expect(exceptionElement.type, typeProvider.objectType);
var stackNode = catchClause.stackTraceParameter as SimpleIdentifier;
var stackElement = stackNode.staticElement as LocalVariableElement;
@@ -7757,7 +7753,7 @@
var exceptionIdentifier =
exceptionStatement.expression as SimpleIdentifier;
expect(exceptionIdentifier.staticElement, same(exceptionElement));
- expect(exceptionIdentifier.staticType, DynamicTypeImpl.instance);
+ expect(exceptionIdentifier.staticType, typeProvider.objectType);
var stackStatement = catchStatements[1] as ExpressionStatement;
var stackIdentifier = stackStatement.expression as SimpleIdentifier;
@@ -7805,7 +7801,7 @@
var exceptionNode = catchClause.exceptionParameter as SimpleIdentifier;
var exceptionElement =
exceptionNode.staticElement as LocalVariableElement;
- expect(exceptionElement.type, DynamicTypeImpl.instance);
+ expect(exceptionElement.type, typeProvider.objectType);
}
// on int catch (e)
@@ -8206,7 +8202,7 @@
var statement = statements[0] as ExpressionStatement;
var creation = statement.expression as InstanceCreationExpression;
- expect(creation.staticType, interfaceTypeStar(randomElement));
+ expect(creation.staticType, interfaceTypeNone(randomElement));
ConstructorName constructorName = creation.constructorName;
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index a04b1a4..97d2b78 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,7 +1,12 @@
# Changelog
+## 6.1.1
+- Callsite `StackTrace`s are now attached to `RPCError`s and `SentinelException`s.
+
+## 6.1.0+1
+- Documentation update.
## 6.1.0
-- *bug fix* Fixed issue where the root object was omitted from
+- *breaking bug fix*: Fixed issue where the root object was omitted from
`HeapSnapshot.classes` and the sentinel `HeapSnapshotObject` was omitted from
`HeapSnapshot.objects`
- Added `identityHashCode` property to `HeapSnapshotObject`, which can be used to compare
@@ -10,6 +15,9 @@
access children of a given object.
- Added `klass` getter to `HeapSnapshotObject`.
- Fixed issue where `null` could be returned instead of `InstanceRef` of type `Null`.
+- Added `getAllocationTraces` and `setTraceClassAllocation` RPCs.
+- Updated `CpuSample` to include `identityHashCode` and `classId` properties.
+- Updated `Class` to include `traceAllocations` property.
## 6.0.1
- Stable null-safe release.
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index e5c1673..4413911 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -505,6 +505,20 @@
Future<AllocationProfile> getAllocationProfile(String isolateId,
{bool? reset, bool? gc});
+ /// The `getAllocationTraces` RPC allows for the retrieval of allocation
+ /// traces for objects of a specific set of types (see
+ /// [setTraceClassAllocation]). Only samples collected in the time range
+ /// `[timeOriginMicros, timeOriginMicros + timeExtentMicros]` will be
+ /// reported.
+ ///
+ /// If `classId` is provided, only traces for allocations with the matching
+ /// `classId` will be reported.
+ ///
+ /// If the profiler is disabled, an RPC error response will be returned.
+ ///
+ /// If isolateId refers to an isolate which has exited, then the Collected
+ /// Sentinel is returned.
+ ///
/// See [CpuSamples].
Future<CpuSamples> getAllocationTraces(
String isolateId, {
@@ -1061,6 +1075,16 @@
/// returned.
Future<Success> setName(String isolateId, String name);
+ /// The `setTraceClassAllocation` RPC allows for enabling or disabling
+ /// allocation tracing for a specific type of object. Allocation traces can be
+ /// retrieved with the `getAllocationTraces` RPC.
+ ///
+ /// If `enable` is true, allocations of objects of the class represented by
+ /// `classId` will be traced.
+ ///
+ /// If `isolateId` refers to an isolate which has exited, then the `Collected`
+ /// [Sentinel] is returned.
+ ///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
@@ -1574,12 +1598,25 @@
}
}
+class _OutstandingRequest<T> {
+ _OutstandingRequest(this.method);
+ static int _idCounter = 0;
+ final String id = '${_idCounter++}';
+ final String method;
+ final StackTrace _stackTrace = StackTrace.current;
+ final Completer<T> _completer = Completer<T>();
+
+ Future<T> get future => _completer.future;
+
+ void complete(T value) => _completer.complete(value);
+ void completeError(Object error) =>
+ _completer.completeError(error, _stackTrace);
+}
+
class VmService implements VmServiceInterface {
late final StreamSubscription _streamSub;
late final Function _writeMessage;
- int _id = 0;
- Map<String, Completer> _completers = {};
- Map<String, String> _methodCalls = {};
+ final Map<String, _OutstandingRequest> _outstandingRequests = {};
Map<String, ServiceCallback> _services = {};
late final Log _log;
@@ -2019,12 +2056,14 @@
Future<void> dispose() async {
await _streamSub.cancel();
- _completers.forEach((id, c) {
- final method = _methodCalls[id];
- return c.completeError(RPCError(
- method, RPCError.kServerError, 'Service connection disposed'));
+ _outstandingRequests.forEach((id, request) {
+ request._completer.completeError(RPCError(
+ request.method,
+ RPCError.kServerError,
+ 'Service connection disposed',
+ ));
});
- _completers.clear();
+ _outstandingRequests.clear();
if (_disposeHandler != null) {
await _disposeHandler!();
}
@@ -2035,21 +2074,19 @@
Future get onDone => _onDoneCompleter.future;
- Future<T> _call<T>(String method, [Map args = const {}]) {
- String id = '${++_id}';
- Completer<T> completer = Completer<T>();
- _completers[id] = completer;
- _methodCalls[id] = method;
+ Future<T> _call<T>(String method, [Map args = const {}]) async {
+ final request = _OutstandingRequest(method);
+ _outstandingRequests[request.id] = request;
Map m = {
'jsonrpc': '2.0',
- 'id': id,
+ 'id': request.id,
'method': method,
'params': args,
};
String message = jsonEncode(m);
_onSend.add(message);
_writeMessage(message);
- return completer.future;
+ return await request.future as T;
}
/// Register a service for invocation.
@@ -2119,22 +2156,21 @@
}
void _processResponse(Map<String, dynamic> json) {
- Completer? completer = _completers.remove(json['id']);
- String methodName = _methodCalls.remove(json['id'])!;
- List<String> returnTypes = _methodReturnTypes[methodName] ?? [];
- if (completer == null) {
+ final request = _outstandingRequests.remove(json['id']);
+ if (request == null) {
_log.severe('unmatched request response: ${jsonEncode(json)}');
} else if (json['error'] != null) {
- completer.completeError(RPCError.parse(methodName, json['error']));
+ request.completeError(RPCError.parse(request.method, json['error']));
} else {
Map<String, dynamic> result = json['result'] as Map<String, dynamic>;
String type = result['type'];
if (type == 'Sentinel') {
- completer.completeError(SentinelException.parse(methodName, result));
+ request.completeError(SentinelException.parse(request.method, result));
} else if (_typeFactories[type] == null) {
- completer.complete(Response.parse(result));
+ request.complete(Response.parse(result));
} else {
- completer.complete(createServiceObject(result, returnTypes));
+ List<String> returnTypes = _methodReturnTypes[request.method] ?? [];
+ request.complete(createServiceObject(result, returnTypes));
}
}
}
@@ -6645,8 +6681,7 @@
/// line number followed by `(tokenPos, columnNumber)` pairs:
///
/// ```
-/// [
-/// ```lineNumber, (tokenPos, columnNumber)*]
+/// [lineNumber, (tokenPos, columnNumber)*]
/// ```
///
/// The `tokenPos` is an arbitrary integer value that is used to represent a
@@ -6656,10 +6691,7 @@
/// For example, a `tokenPosTable` with the value...
///
/// ```
-/// [
-/// ```[
-/// ```1, 100, 5, 101, 8],[
-/// ```2, 102, 7]]
+/// [[1, 100, 5, 101, 8],[2, 102, 7]]
/// ```
///
/// ...encodes the mapping:
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index f934b81..b3e0010 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
A library to communicate with a service implementing the Dart VM
service protocol.
-version: 6.1.0
+version: 6.1.1
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
diff --git a/pkg/vm_service/test/rpc_error_test.dart b/pkg/vm_service/test/rpc_error_test.dart
new file mode 100644
index 0000000..e6b5df5
--- /dev/null
+++ b/pkg/vm_service/test/rpc_error_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, 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:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/test_helper.dart';
+
+var tests = <VMTest>[
+ (VmService vm) async {
+ // Invoke a non-existant RPC.
+ try {
+ final res = await vm.callMethod('foo');
+ fail('Expected RPCError, got $res');
+ } on RPCError catch (e, st) {
+ // Ensure stack trace contains actual invocation path.
+ final stack = st.toString().split('\n');
+ expect(stack.where((e) => e.contains('VmService.callMethod')).length, 1);
+ // Call to vm.callMethod('foo') and the invocation of the test closure.
+ expect(
+ stack.where((e) => e.contains('test/rpc_error_test.dart')).length, 2);
+ } catch (e) {
+ fail('Expected RPCError, got $e');
+ }
+ },
+];
+
+main([args = const <String>[]]) async => await runVMTests(
+ args,
+ tests,
+ 'rpc_error_test.dart',
+ );
diff --git a/pkg/vm_service/test/throws_sentinel_test.dart b/pkg/vm_service/test/throws_sentinel_test.dart
index 3b377c5..fd95bbe 100644
--- a/pkg/vm_service/test/throws_sentinel_test.dart
+++ b/pkg/vm_service/test/throws_sentinel_test.dart
@@ -12,8 +12,15 @@
try {
final res = await vm.getIsolate('isolates/12321');
fail('Expected SentinelException, got $res');
- } on SentinelException {
- // Expected.
+ } on SentinelException catch (e, st) {
+ // Ensure stack trace contains actual invocation path.
+ final stack = st.toString().split('\n');
+ expect(stack.where((e) => e.contains('VmService.getIsolate')).length, 1);
+ // Call to vm.getIsolate('isolates/12321') and the invocation of the test closure.
+ expect(
+ stack.where((e) => e.contains('test/throws_sentinel_test.dart')).length,
+ 2,
+ );
} catch (e) {
fail('Expected SentinelException, got $e');
}
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index f512f5a..774f271 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -100,12 +100,11 @@
Future<void> dispose() async {
await _streamSub.cancel();
- _completers.forEach((id, c) {
- final method = _methodCalls[id];
- return c.completeError(RPCError(
- method, RPCError.kServerError, 'Service connection disposed'));
+ _outstandingRequests.forEach((id, request) {
+ request._completer.completeError(RPCError(
+ request.method, RPCError.kServerError, 'Service connection disposed',));
});
- _completers.clear();
+ _outstandingRequests.clear();
if (_disposeHandler != null) {
await _disposeHandler!();
}
@@ -116,16 +115,14 @@
Future get onDone => _onDoneCompleter.future;
- Future<T> _call<T>(String method, [Map args = const {}]) {
- String id = '${++_id}';
- Completer<T> completer = Completer<T>();
- _completers[id] = completer;
- _methodCalls[id] = method;
- Map m = {'jsonrpc': '2.0', 'id': id, 'method': method, 'params': args,};
+ Future<T> _call<T>(String method, [Map args = const {}]) async {
+ final request = _OutstandingRequest(method);
+ _outstandingRequests[request.id] = request;
+ Map m = {'jsonrpc': '2.0', 'id': request.id, 'method': method, 'params': args,};
String message = jsonEncode(m);
_onSend.add(message);
_writeMessage(message);
- return completer.future;
+ return await request.future as T;
}
/// Register a service for invocation.
@@ -196,22 +193,21 @@
}
void _processResponse(Map<String, dynamic> json) {
- Completer? completer = _completers.remove(json['id']);
- String methodName = _methodCalls.remove(json['id'])!;
- List<String> returnTypes = _methodReturnTypes[methodName] ?? [];
- if (completer == null) {
+ final request = _outstandingRequests.remove(json['id']);
+ if (request == null) {
_log.severe('unmatched request response: ${jsonEncode(json)}');
} else if (json['error'] != null) {
- completer.completeError(RPCError.parse(methodName, json['error']));
+ request.completeError(RPCError.parse(request.method, json['error']));
} else {
Map<String, dynamic> result = json['result'] as Map<String, dynamic>;
String type = result['type'];
if (type == 'Sentinel') {
- completer.completeError(SentinelException.parse(methodName, result));
+ request.completeError(SentinelException.parse(request.method, result));
} else if (_typeFactories[type] == null) {
- completer.complete(Response.parse(result));
+ request.complete(Response.parse(result));
} else {
- completer.complete(createServiceObject(result, returnTypes));
+ List<String> returnTypes = _methodReturnTypes[request.method] ?? [];
+ request.complete(createServiceObject(result, returnTypes));
}
}
}
@@ -816,13 +812,29 @@
gen.write('}');
gen.writeln();
+ gen.write('''
+class _OutstandingRequest<T> {
+ _OutstandingRequest(this.method);
+ static int _idCounter = 0;
+ final String id = '\${_idCounter++}';
+ final String method;
+ final StackTrace _stackTrace = StackTrace.current;
+ final Completer<T> _completer = Completer<T>();
+
+ Future<T> get future => _completer.future;
+
+ void complete(T value) => _completer.complete(value);
+ void completeError(Object error) =>
+ _completer.completeError(error, _stackTrace);
+}
+''');
+
// The client side service implementation.
gen.writeStatement('class VmService implements VmServiceInterface {');
gen.writeStatement('late final StreamSubscription _streamSub;');
gen.writeStatement('late final Function _writeMessage;');
- gen.writeStatement('int _id = 0;');
- gen.writeStatement('Map<String, Completer> _completers = {};');
- gen.writeStatement('Map<String, String> _methodCalls = {};');
+ gen.writeStatement(
+ 'final Map<String, _OutstandingRequest> _outstandingRequests = {};');
gen.writeStatement('Map<String, ServiceCallback> _services = {};');
gen.writeStatement('late final Log _log;');
gen.write('''
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 0dfaa61..938b2d9 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -1009,18 +1009,15 @@
// RoundedAllocationSize(
// (array_length * kwordSize) + target::Array::header_size()).
__ mov(R3, Operand(R2)); // Array length.
- // Check that length is a positive Smi.
+ // Check that length is a Smi.
__ tst(R3, Operand(kSmiTagMask));
__ b(&slow_case, NE);
- __ cmp(R3, Operand(0));
- __ b(&slow_case, LT);
-
- // Check for maximum allowed length.
+ // Check length >= 0 && length <= kMaxNewSpaceElements
const intptr_t max_len =
target::ToRawSmi(target::Array::kMaxNewSpaceElements);
__ CompareImmediate(R3, max_len);
- __ b(&slow_case, GT);
+ __ b(&slow_case, HI);
const intptr_t cid = kArrayCid;
NOT_IN_PRODUCT(__ LoadAllocationStatsAddress(R4, cid));
@@ -3450,13 +3447,11 @@
/* R2: requested array length argument. */
__ tst(R2, Operand(kSmiTagMask));
__ b(&call_runtime, NE);
- __ CompareImmediate(R2, 0);
- __ b(&call_runtime, LT);
__ SmiUntag(R2);
- /* Check for maximum allowed length. */
+ /* Check for length >= 0 && length <= max_len. */
/* R2: untagged array length. */
__ CompareImmediate(R2, max_len);
- __ b(&call_runtime, GT);
+ __ b(&call_runtime, HI);
__ mov(R2, Operand(R2, LSL, scale_shift));
const intptr_t fixed_size_plus_alignment_padding =
target::TypedData::InstanceSize() +
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 01b54d7..35c6bcf 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1128,18 +1128,14 @@
// and is computed as:
// RoundedAllocationSize(
// (array_length * kwordSize) + target::Array::header_size()).
- // Assert that length is a Smi.
- __ tsti(R2, Immediate(kSmiTagMask));
- __ b(&slow_case, NE);
+ // Check that length is a Smi.
+ __ BranchIfNotSmi(R2, &slow_case);
- __ cmp(R2, Operand(0));
- __ b(&slow_case, LT);
-
- // Check for maximum allowed length.
+ // Check length >= 0 && length <= kMaxNewSpaceElements
const intptr_t max_len =
target::ToRawSmi(target::Array::kMaxNewSpaceElements);
__ CompareImmediate(R2, max_len);
- __ b(&slow_case, GT);
+ __ b(&slow_case, HI);
const intptr_t cid = kArrayCid;
NOT_IN_PRODUCT(__ MaybeTraceAllocation(kArrayCid, R4, &slow_case));
@@ -3594,13 +3590,11 @@
/* Check that length is a positive Smi. */
/* R2: requested array length argument. */
__ BranchIfNotSmi(R2, &call_runtime);
- __ CompareRegisters(R2, ZR);
- __ b(&call_runtime, LT);
__ SmiUntag(R2);
- /* Check for maximum allowed length. */
+ /* Check for length >= 0 && length <= max_len. */
/* R2: untagged array length. */
__ CompareImmediate(R2, max_len);
- __ b(&call_runtime, GT);
+ __ b(&call_runtime, HI);
__ LslImmediate(R2, R2, scale_shift);
const intptr_t fixed_size_plus_alignment_padding =
target::TypedData::InstanceSize() +
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 229be66..7dcc08e 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -804,25 +804,21 @@
// Uses EAX, EBX, ECX, EDI as temporary registers.
// The newly allocated object is returned in EAX.
void StubCodeCompiler::GenerateAllocateArrayStub(Assembler* assembler) {
- Label slow_case;
- // Compute the size to be allocated, it is based on the array length
- // and is computed as:
- // RoundedAllocationSize(
- // (array_length * kwordSize) + target::Array::header_size()).
- // Assert that length is a Smi.
- __ testl(EDX, Immediate(kSmiTagMask));
-
if (!FLAG_use_slow_path) {
+ Label slow_case;
+ // Compute the size to be allocated, it is based on the array length
+ // and is computed as:
+ // RoundedAllocationSize(
+ // (array_length * kwordSize) + target::Array::header_size()).
+ // Assert that length is a Smi.
+ __ testl(EDX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &slow_case);
- __ cmpl(EDX, Immediate(0));
- __ j(LESS, &slow_case);
-
// Check for maximum allowed length.
const Immediate& max_len =
Immediate(target::ToRawSmi(target::Array::kMaxNewSpaceElements));
__ cmpl(EDX, max_len);
- __ j(GREATER, &slow_case);
+ __ j(ABOVE, &slow_case);
NOT_IN_PRODUCT(__ MaybeTraceAllocation(kArrayCid, EAX, &slow_case,
Assembler::kFarJump));
@@ -2948,13 +2944,11 @@
/* EDI: requested array length argument. */
__ testl(EDI, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &call_runtime);
- __ cmpl(EDI, Immediate(0));
- __ j(LESS, &call_runtime);
__ SmiUntag(EDI);
- /* Check for maximum allowed length. */
+ /* Check for length >= 0 && length <= max_len. */
/* EDI: untagged array length. */
__ cmpl(EDI, Immediate(max_len));
- __ j(GREATER, &call_runtime);
+ __ j(ABOVE, &call_runtime);
/* Special case for scaling by 16. */
if (scale_factor == TIMES_16) {
/* double length of array. */
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index e852d5a..9141a0c 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -1060,17 +1060,15 @@
// RoundedAllocationSize(
// (array_length * target::kwordSize) + target::Array::header_size()).
__ movq(RDI, R10); // Array Length.
- // Check that length is a positive Smi.
+ // Check that length is Smi.
__ testq(RDI, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &slow_case);
- __ cmpq(RDI, Immediate(0));
- __ j(LESS, &slow_case);
- // Check for maximum allowed length.
+ // Check length >= 0 && length <= kMaxNewSpaceElements
const Immediate& max_len =
Immediate(target::ToRawSmi(target::Array::kMaxNewSpaceElements));
__ cmpq(RDI, max_len);
- __ j(GREATER, &slow_case);
+ __ j(ABOVE, &slow_case);
// Check for allocation tracing.
NOT_IN_PRODUCT(
@@ -3538,13 +3536,11 @@
/* RDI: requested array length argument. */
__ testq(RDI, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &call_runtime);
- __ cmpq(RDI, Immediate(0));
- __ j(LESS, &call_runtime);
__ SmiUntag(RDI);
- /* Check for maximum allowed length. */
+ /* Check for length >= 0 && length <= max_len. */
/* RDI: untagged array length. */
__ cmpq(RDI, Immediate(max_len));
- __ j(GREATER, &call_runtime);
+ __ j(ABOVE, &call_runtime);
/* Special case for scaling by 16. */
if (scale_factor == TIMES_16) {
/* double length of array. */
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 9df4745..e6df0a6 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -732,6 +732,10 @@
### getAllocationTraces
+```
+CpuSamples getAllocationTraces(string isolateId, int timeOriginMicros [optional], int timeExtentMicros [optional], string classId [optional])
+```
+
The _getAllocationTraces_ RPC allows for the retrieval of allocation traces for objects of a
specific set of types (see [setTraceClassAllocation](#setTraceClassAllocation)). Only samples
collected in the time range `[timeOriginMicros, timeOriginMicros + timeExtentMicros]` will be
@@ -744,10 +748,6 @@
If isolateId refers to an isolate which has exited, then the Collected Sentinel is returned.
-```
-CpuSamples getAllocationTraces(string isolateId, int timeOriginMicros [optional], int timeExtentMicros [optional], string classId [optional])
-```
-
See [CpuSamples](#cpusamples).
### getClassList
@@ -1385,16 +1385,16 @@
### setTraceClassAllocation
+```
+Success|Sentinel setTraceClassAllocation(string isolateId, string classId, bool enable)
+```
+
The _setTraceClassAllocation_ RPC allows for enabling or disabling allocation tracing for a specific type of object. Allocation traces can be retrieved with the _getAllocationTraces_ RPC.
If `enable` is true, allocations of objects of the class represented by `classId` will be traced.
If `isolateId` refers to an isolate which has exited, then the _Collected_ [Sentinel](#sentinel) is returned.
-```
-Success|Sentinel setTraceClassAllocation(string isolateId, string classId, bool enable)
-```
-
See [Success](#success).
### setVMName
@@ -3996,8 +3996,6 @@
3.40 | Added `IsolateFlag` object and `isolateFlags` property to `Isolate`.
3.41 | Added `PortList` object, `ReceivePort` `InstanceKind`, and `getPorts` RPC.
3.42 | Added `limit` optional parameter to `getStack` RPC.
-3.43 | Updated heap snapshot format to include identity hash codes. Added `getAllocationTraces`
-and `setTraceClassAllocation` RPCs, updated `CpuSample` to include `identityHashCode` and
-`classId` properties, updated `Class` to include `traceAllocations` property.
+3.43 | Updated heap snapshot format to include identity hash codes. Added `getAllocationTraces` and `setTraceClassAllocation` RPCs, updated `CpuSample` to include `identityHashCode` and `classId` properties, updated `Class` to include `traceAllocations` property.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/tools/VERSION b/tools/VERSION
index c32fee5..c251049 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 56
+PRERELEASE 57
PRERELEASE_PATCH 0
\ No newline at end of file