Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b8f4018

Browse files
author
Dart CI
committed
Version 2.14.0-74.0.dev
Merge commit '9ff5ac12c6efec99b8d6580c7a7154bb5e265add' into 'dev'
2 parents 6dd4fe9 + 9ff5ac1 commit b8f4018

File tree

48 files changed

+3983
-27
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3983
-27
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6146,7 +6146,7 @@ class Parser {
61466146
potentialTypeArg ??= computeTypeParamOrArg(token);
61476147
afterToken ??= potentialTypeArg.skip(token).next!;
61486148
TypeParamOrArgInfo typeArg;
6149-
if (optional('(', afterToken)) {
6149+
if (optional('(', afterToken) && !potentialTypeArg.recovered) {
61506150
typeArg = potentialTypeArg;
61516151
} else {
61526152
typeArg = noTypeParamOrArg;

pkg/_fe_analyzer_shared/lib/src/parser/type_info.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ abstract class TypeParamOrArgInfo {
7878
/// Return the number of type arguments
7979
int get typeArgumentCount;
8080

81+
bool get recovered => false;
82+
8183
/// Return the simple type associated with this simple type argument
8284
/// or throw an exception if this is not a simple type argument.
8385
TypeInfo get typeInfo {
@@ -346,5 +348,7 @@ TypeParamOrArgInfo computeTypeParamOrArg(Token token,
346348
/// possible other constructs will pass (e.g., 'a < C, D > 3').
347349
TypeParamOrArgInfo computeMethodTypeArguments(Token token) {
348350
TypeParamOrArgInfo typeArg = computeTypeParamOrArg(token);
349-
return optional('(', typeArg.skip(token).next!) ? typeArg : noTypeParamOrArg;
351+
return optional('(', typeArg.skip(token).next!) && !typeArg.recovered
352+
? typeArg
353+
: noTypeParamOrArg;
350354
}

pkg/_fe_analyzer_shared/lib/src/parser/type_info_impl.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,9 @@ class ComplexTypeParamOrArgInfo extends TypeParamOrArgInfo {
923923
/// and may not be part of the token stream.
924924
Token? skipEnd;
925925

926+
@override
927+
bool recovered = false;
928+
926929
ComplexTypeParamOrArgInfo(
927930
Token token, this.inDeclaration, this.allowsVariance)
928931
: assert(optional('<', token.next!)),
@@ -988,6 +991,7 @@ class ComplexTypeParamOrArgInfo extends TypeParamOrArgInfo {
988991
// Recovery
989992
skipEnd = splitCloser(next);
990993
if (skipEnd == null) {
994+
recovered = true;
991995
if (optional('(', next)) {
992996
token = next.endGroup!;
993997
next = token.next!;

pkg/analysis_server/lib/src/context_manager.dart

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,9 @@ class ContextManagerImpl implements ContextManager {
460460
for (var path in watched.paths) {
461461
bazelWatcherService!.stopWatching(watched.workspace, path);
462462
}
463+
_stopWatchingBazelBinPaths(watched);
463464
}
465+
bazelSearchSubscriptions.remove(rootFolder)?.cancel();
464466
driverMap.remove(rootFolder);
465467
}
466468

@@ -474,6 +476,11 @@ class ContextManagerImpl implements ContextManager {
474476
}
475477
}
476478

479+
List<String> _getPossibelBazelBinPaths(_BazelWatchedFiles watched) => [
480+
pathContext.join(watched.workspace, 'bazel-bin'),
481+
pathContext.join(watched.workspace, 'blaze-bin'),
482+
];
483+
477484
/// Establishes watch(es) for the Bazel generated files provided in
478485
/// [notification].
479486
///
@@ -493,10 +500,23 @@ class ContextManagerImpl implements ContextManager {
493500
}
494501

495502
/// Notifies the drivers that a generated Bazel file has changed.
496-
void _handleBazelWatchEvents(List<WatchEvent> events) {
503+
void _handleBazelWatchEvents(List<WatchEvent> allEvents) {
504+
// First check if we have any changes to the bazel-*/blaze-* paths. If
505+
// we do, we'll simply recreate all contexts to make sure that we follow the
506+
// correct paths.
507+
var bazelSymlinkPaths = bazelWatchedPathsPerFolder.values
508+
.expand((watched) => _getPossibelBazelBinPaths(watched))
509+
.toSet();
510+
if (allEvents.any((event) => bazelSymlinkPaths.contains(event.path))) {
511+
refresh();
512+
return;
513+
}
514+
515+
var fileEvents =
516+
allEvents.where((event) => !bazelSymlinkPaths.contains(event.path));
497517
for (var driver in driverMap.values) {
498518
var needsUriReset = false;
499-
for (var event in events) {
519+
for (var event in fileEvents) {
500520
if (event.type == ChangeType.ADD) {
501521
driver.addFile(event.path);
502522
needsUriReset = true;
@@ -599,6 +619,28 @@ class ContextManagerImpl implements ContextManager {
599619
existingExcludedSet.containsAll(excludedPaths);
600620
}
601621

622+
/// Starts watching for the `bazel-bin` and `blaze-bin` symlinks.
623+
///
624+
/// This is important since these symlinks might not be present when the
625+
/// server starts up, in which case `BazelWorkspace` assumes by default the
626+
/// Bazel ones. So we want to detect if the symlinks get created to reset
627+
/// everything and repeat the search for the folders.
628+
void _startWatchingBazelBinPaths(_BazelWatchedFiles watched) {
629+
var watcherService = bazelWatcherService;
630+
if (watcherService == null) return;
631+
var paths = _getPossibelBazelBinPaths(watched);
632+
watcherService.startWatching(
633+
watched.workspace, BazelSearchInfo(paths[0], paths));
634+
}
635+
636+
/// Stops watching for the `bazel-bin` and `blaze-bin` symlinks.
637+
void _stopWatchingBazelBinPaths(_BazelWatchedFiles watched) {
638+
var watcherService = bazelWatcherService;
639+
if (watcherService == null) return;
640+
var paths = _getPossibelBazelBinPaths(watched);
641+
watcherService.stopWatching(watched.workspace, paths[0]);
642+
}
643+
602644
/// Listens to files generated by Bazel that were found or searched for.
603645
///
604646
/// This is handled specially because the files are outside the package
@@ -607,13 +649,19 @@ class ContextManagerImpl implements ContextManager {
607649
/// Does nothing if the [driver] is not in a Bazel workspace.
608650
void _watchBazelFilesIfNeeded(Folder folder, AnalysisDriver analysisDriver) {
609651
if (!experimentalEnableBazelWatching) return;
652+
var watcherService = bazelWatcherService;
653+
if (watcherService == null) return;
654+
610655
var workspace = analysisDriver.analysisContext?.contextRoot.workspace;
611656
if (workspace is BazelWorkspace &&
612657
!bazelSearchSubscriptions.containsKey(folder)) {
613-
var searchSubscription = workspace.bazelCandidateFiles.listen(
658+
bazelSearchSubscriptions[folder] = workspace.bazelCandidateFiles.listen(
614659
(notification) =>
615660
_handleBazelSearchInfo(folder, workspace.root, notification));
616-
bazelSearchSubscriptions[folder] = searchSubscription;
661+
662+
var watched = _BazelWatchedFiles(workspace.root);
663+
bazelWatchedPathsPerFolder[folder] = watched;
664+
_startWatchingBazelBinPaths(watched);
617665
}
618666
}
619667
}

pkg/analysis_server/test/integration/server/bazel_changes_test.dart

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ class BazelChangesTest extends AbstractAnalysisServerIntegrationTest {
3535
late String bazelRoot;
3636
late String tmpPath;
3737
late String workspacePath;
38-
late String bazelOutPath;
39-
late String bazelBinPath;
40-
late String bazelGenfilesPath;
38+
late String bazelOrBlazeOutPath;
39+
late String bazelOrBlazeBinPath;
40+
late String bazelOrBlazeGenfilesPath;
4141
late Directory oldSourceDirectory;
4242

4343
String inTmpDir(String relative) =>
@@ -61,21 +61,19 @@ class BazelChangesTest extends AbstractAnalysisServerIntegrationTest {
6161
sourceDirectory = Directory(inWorkspace('third_party/dart/project'));
6262
sourceDirectory.createSync(recursive: true);
6363

64-
bazelRoot = inTmpDir('bazel_root');
64+
bazelRoot = inTmpDir('bazel_or_blaze_root');
6565
Directory(bazelRoot).createSync(recursive: true);
6666

67-
bazelOutPath = '$bazelRoot/execroot/bazel_workspace/bazel-out';
68-
bazelBinPath = '$bazelRoot/execroot/bazel_workspace/bazel-out/bin';
69-
bazelGenfilesPath =
70-
'$bazelRoot/execroot/bazel_workspace/bazel-out/genfiles';
67+
bazelOrBlazeOutPath =
68+
'$bazelRoot/execroot/bazel_or_blaze_workspace/bazel_or_blaze-out';
69+
bazelOrBlazeBinPath =
70+
'$bazelRoot/execroot/bazel_or_blaze_workspace/bazel_or_blaze-out/bin';
71+
bazelOrBlazeGenfilesPath =
72+
'$bazelRoot/execroot/bazel_or_blaze_workspace/bazel_or_blaze-out/genfiles';
7173

72-
Directory(inTmpDir(bazelOutPath)).createSync(recursive: true);
73-
Directory(inTmpDir(bazelBinPath)).createSync(recursive: true);
74-
Directory(inTmpDir(bazelGenfilesPath)).createSync(recursive: true);
75-
76-
Link(inWorkspace('bazel-out')).createSync(bazelOutPath);
77-
Link(inWorkspace('bazel-bin')).createSync(bazelBinPath);
78-
Link(inWorkspace('bazel-genfiles')).createSync(bazelGenfilesPath);
74+
Directory(inTmpDir(bazelOrBlazeOutPath)).createSync(recursive: true);
75+
Directory(inTmpDir(bazelOrBlazeBinPath)).createSync(recursive: true);
76+
Directory(inTmpDir(bazelOrBlazeGenfilesPath)).createSync(recursive: true);
7977

8078
commandLogPath = inTmpDir('$bazelRoot/command.log');
8179
}
@@ -91,6 +89,17 @@ class BazelChangesTest extends AbstractAnalysisServerIntegrationTest {
9189
// not run from a snapshot.
9290
@TestTimeout(Timeout.factor(2))
9391
Future<void> test_bazelChanges() async {
92+
await testChangesImpl('bazel');
93+
}
94+
95+
// Add a bit more time -- the isolate take a while to start when the test is
96+
// not run from a snapshot.
97+
@TestTimeout(Timeout.factor(2))
98+
Future<void> test_blazeChanges() async {
99+
await testChangesImpl('blaze');
100+
}
101+
102+
Future<void> testChangesImpl(String prefix) async {
94103
var testFile = inWorkspace('${sourceDirectory.path}/lib/test.dart');
95104

96105
var errors = <AnalysisError>[];
@@ -126,8 +135,9 @@ void main() { my_fun(); }
126135

127136
await resetCompleterAndErrors();
128137
var generatedFilePath = inWorkspace(
129-
'$bazelGenfilesPath/third_party/dart/project/lib/generated.dart');
138+
'$bazelOrBlazeGenfilesPath/third_party/dart/project/lib/generated.dart');
130139
writeFile(generatedFilePath, 'my_fun() {}');
140+
_createSymlinks(prefix);
131141
writeFile(commandLogPath, 'Build completed successfully');
132142

133143
await processedNotification.future;
@@ -145,6 +155,7 @@ void main() { my_fun(); }
145155
// Now delete the file completely.
146156
await resetCompleterAndErrors();
147157
File(generatedFilePath).deleteSync();
158+
_deleteSymlinks(prefix);
148159
writeFile(commandLogPath, 'Build did NOT complete successfully');
149160

150161
await processedNotification.future;
@@ -153,9 +164,22 @@ void main() { my_fun(); }
153164
// And finally re-add the correct file -- errors should go away once again.
154165
await resetCompleterAndErrors();
155166
writeFile(generatedFilePath, 'my_fun() {}');
167+
_createSymlinks(prefix);
156168
writeFile(commandLogPath, 'Build completed successfully');
157169

158170
await processedNotification.future;
159171
expect(errors, isEmpty);
160172
}
173+
174+
void _createSymlinks(String prefix) {
175+
Link(inWorkspace('$prefix-out')).createSync(bazelOrBlazeOutPath);
176+
Link(inWorkspace('$prefix-bin')).createSync(bazelOrBlazeBinPath);
177+
Link(inWorkspace('$prefix-genfiles')).createSync(bazelOrBlazeGenfilesPath);
178+
}
179+
180+
void _deleteSymlinks(String prefix) {
181+
Link(inWorkspace('$prefix-out')).deleteSync();
182+
Link(inWorkspace('$prefix-bin')).deleteSync();
183+
Link(inWorkspace('$prefix-genfiles')).deleteSync();
184+
}
161185
}

pkg/analyzer/lib/src/workspace/bazel_watcher.dart

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,21 @@ class BazelFilePoller {
110110
/// exist.
111111
_TimestampAndLength? _pollOne(String path) {
112112
try {
113-
var file = _provider.getFile(path);
114-
var timestamp = file.modificationStamp;
115-
var length = file.lengthSync;
116-
return _TimestampAndLength(timestamp, length);
113+
// This might seem a bit convoluted but is necessary to deal with a
114+
// symlink to a directory (e.g., `bazel-bin`).
115+
var resource = _provider.getResource(
116+
_provider.getResource(path).resolveSymbolicLinksSync().path);
117+
if (resource is File) {
118+
var timestamp = resource.modificationStamp;
119+
var length = resource.lengthSync;
120+
return _TimestampAndLength(timestamp, length);
121+
} else if (resource is Folder) {
122+
// `ResourceProvider` doesn't currently support getting timestamps of a
123+
// folder, so we use a dummy value here. But it's still useful: this
124+
// will correctly generate `ADD` or `REMOVE` events (we'll be just
125+
// unable to generate any `CHANGE` events).
126+
return _TimestampAndLength(0, 0);
127+
}
117128
} on FileSystemException catch (_) {
118129
// File doesn't exist, so return null.
119130
return null;

pkg/analyzer/test/src/workspace/bazel_watcher_test.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,47 @@ class BazelWatcherTest with ResourceProviderMixin {
233233
recPort.close();
234234
}
235235

236+
void test_bazelFileWatcherWithFolder() async {
237+
_addResources([
238+
'/workspace/WORKSPACE',
239+
]);
240+
241+
// The `_addResources`/`_deleteResources` functions recognize a folder by a
242+
// trailing `/`, but everywhere else we need to use normalized paths.
243+
var addFolder = (path) => _addResources(['$path/']);
244+
var deleteFolder = (path) => _deleteResources(['$path/']);
245+
246+
var candidates = [
247+
convertPath('/workspace/bazel-out'),
248+
convertPath('/workspace/blaze-out'),
249+
];
250+
var watcher = BazelFilePoller(resourceProvider, candidates);
251+
252+
// First do some tests with the first candidate path.
253+
addFolder(candidates[0]);
254+
var event = watcher.poll()!;
255+
256+
expect(event.type, ChangeType.ADD);
257+
expect(event.path, candidates[0]);
258+
259+
deleteFolder(candidates[0]);
260+
event = watcher.poll()!;
261+
262+
expect(event.type, ChangeType.REMOVE);
263+
expect(event.path, candidates[0]);
264+
265+
// Now check that if we add the *second* candidate, we'll get the
266+
// notification for it.
267+
addFolder(candidates[1]);
268+
event = watcher.poll()!;
269+
270+
expect(event.type, ChangeType.ADD);
271+
expect(event.path, candidates[1]);
272+
273+
// Next poll should be `null` since there were no changes.
274+
expect(watcher.poll(), isNull);
275+
}
276+
236277
/// Create new files and directories from [paths].
237278
void _addResources(List<String> paths) {
238279
for (String path in paths) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
void f(bool b, int i) {
2+
print('b=$b, i=$i');
3+
}
4+
5+
g(int x, int y, Object o) {
6+
f(x < y, (o as Function)());
7+
}
8+
9+
main() {
10+
g(0, 1, () => 2);
11+
}

0 commit comments

Comments
 (0)