Version 2.12.0-246.0.dev
Merge commit 'b94adb2bc2d53897a727e3ae1dcdf10c74948659' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 612de52..6dbb243 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -998,14 +998,14 @@
message: r"""Compiling with sound null safety""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeCompilingWithUnsoundNullSafety =
- messageCompilingWithUnsoundNullSafety;
+const Code<Null> codeCompilingWithoutSoundNullSafety =
+ messageCompilingWithoutSoundNullSafety;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageCompilingWithUnsoundNullSafety = const MessageCode(
- "CompilingWithUnsoundNullSafety",
+const MessageCode messageCompilingWithoutSoundNullSafety = const MessageCode(
+ "CompilingWithoutSoundNullSafety",
severity: Severity.info,
- message: r"""Compiling with unsound null safety""");
+ message: r"""Compiling without sound null safety""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 0bce9d2..3bf7d05 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -109,7 +109,7 @@
<body>
<h1>Analysis Server API Specification</h1>
<h1 style="color:#999999">Version
- 1.32.1
+ 1.32.2
</h1>
<p>
This document contains a specification of the API provided by the
@@ -236,9 +236,13 @@
ignoring the item or treating it with some default/fallback handling.
</p>
<h3>Changelog</h3>
+<h4>1.32.2</h4>
+<ul>
+ <li>Added <tt>FoldingKind.COMMENT</tt> for folding regions for blocks of comments.</li>
+</ul>
<h4>1.32.1</h4>
<ul>
- <li>Added <tt>CompletionSuggestionKind.PACKAGE_NAME</tt> for Pub package name completions in <tt>pubspec.yaml</tt></li>
+ <li>Added <tt>CompletionSuggestionKind.PACKAGE_NAME</tt> for Pub package name completions in <tt>pubspec.yaml</tt>.</li>
</ul>
<h3>Domains</h3>
<p>
@@ -4226,7 +4230,7 @@
An enumeration of the kinds of folding regions.
</p>
- <dl><dt class="value">ANNOTATIONS</dt><dt class="value">BLOCK</dt><dt class="value">CLASS_BODY</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd>
+ <dl><dt class="value">ANNOTATIONS</dt><dt class="value">BLOCK</dt><dt class="value">CLASS_BODY</dt><dt class="value">COMMENT</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd>
<p>
A description of a region that can be folded.
</p>
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index 4ad5e63..68ea10f 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
// To regenerate the file, use the script
// "pkg/analysis_server/tool/spec/generate_files".
-const String PROTOCOL_VERSION = '1.32.1';
+const String PROTOCOL_VERSION = '1.32.2';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analysis_server/lib/src/computer/computer_folding.dart b/pkg/analysis_server/lib/src/computer/computer_folding.dart
index 725ea9a..2a8bc96 100644
--- a/pkg/analysis_server/lib/src/computer/computer_folding.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_folding.dart
@@ -49,7 +49,6 @@
/// Returns a list of folding regions, not `null`.
List<FoldingRegion> compute() {
- _addFileHeaderRegion();
_unit.accept(_DartUnitFoldingComputerVisitor(this));
if (_firstDirective != null &&
@@ -61,39 +60,90 @@
_lastDirective.end - _firstDirective.keyword.end));
}
+ _addCommentRegions();
+
return _foldingRegions;
}
- void _addFileHeaderRegion() {
- var firstToken = _unit.beginToken;
- while (firstToken?.type == TokenType.SCRIPT_TAG) {
- firstToken = firstToken.next;
+ /// Create a folding region for the provided comment, reading forwards if neccesary.
+ ///
+ /// If [mayBeFileHeader] is true, the token will be considered a file header
+ /// if comment is a single-line-comment and there is a blank line or another
+ /// comment type after it.
+ ///
+ /// Returns the next comment to be processed or null if there are no more comments
+ /// to process in the chain.
+ Token _addCommentRegion(Token commentToken, {bool mayBeFileHeader = false}) {
+ int offset, end;
+ var isFileHeader = false;
+ Token nextComment;
+
+ if (commentToken.type == TokenType.MULTI_LINE_COMMENT) {
+ // Multiline comments already span all of their lines but the folding
+ // region should start at the end of the first line.
+ offset = commentToken.offset + (commentToken.eolOffset ?? 0);
+ end = commentToken.end;
+ nextComment = commentToken.next;
+ } else {
+ // Single line comments need grouping together explicitly but should
+ // only group if the prefix is the same and up to any blank line.
+ final isTripleSlash = commentToken.isTripleSlash;
+ // Track the last comment that belongs to this folding region.
+ var lastComment = commentToken;
+ var current = lastComment.next;
+ while (current != null &&
+ current.type == lastComment.type &&
+ current.isTripleSlash == isTripleSlash &&
+ !_hasBlankLineBetween(lastComment.end, current.offset)) {
+ lastComment = current;
+ current = current.next;
+ }
+
+ // For single line comments we prefer to start the range at the end of
+ // first token so the first line is still visible when the range is
+ // collapsed.
+ offset = commentToken.end;
+ end = lastComment.end;
+ nextComment = lastComment.next;
+
+ // Single line comments are file headers if they're followed by a different
+ // comment type of there's a blank line between them and the first token.
+ isFileHeader = mayBeFileHeader &&
+ (nextComment != null ||
+ _hasBlankLineBetween(end, _unit.beginToken.offset));
}
- final Token firstComment = firstToken?.precedingComments;
- if (firstComment == null ||
- firstComment.type != TokenType.SINGLE_LINE_COMMENT) {
- return;
+ final kind = isFileHeader
+ ? FoldingKind.FILE_HEADER
+ : (commentToken.lexeme.startsWith('///') ||
+ commentToken.lexeme.startsWith('/**'))
+ ? FoldingKind.DOCUMENTATION_COMMENT
+ : FoldingKind.COMMENT;
+
+ _addRegion(offset, end, kind);
+
+ return nextComment;
+ }
+
+ void _addCommentRegions() {
+ var token = _unit.beginToken;
+ if (token.type == TokenType.SCRIPT_TAG) {
+ token = token.next;
}
-
- // Walk through the comments looking for a blank line to signal the end of
- // the file header.
- var lastComment = firstComment;
- while (lastComment.next != null) {
- lastComment = lastComment.next;
-
- // If we ran out of tokens, use the original token as starting position.
- final hasBlankLine =
- _hasBlankLineBetween(lastComment, lastComment.next ?? firstToken);
-
- // Also considered non-single-line-comments as the end
- final nextCommentIsDifferentType = lastComment.next != null &&
- lastComment.next.type != TokenType.SINGLE_LINE_COMMENT;
-
- if (hasBlankLine || nextCommentIsDifferentType) {
- _addRegion(firstComment.end, lastComment.end, FoldingKind.FILE_HEADER);
+ var isFirstToken = true;
+ while (token != null) {
+ Token commentToken = token.precedingComments;
+ while (commentToken != null) {
+ commentToken =
+ _addCommentRegion(commentToken, mayBeFileHeader: isFirstToken);
+ }
+ isFirstToken = false;
+ // Only exit the loop when hitting EOF *after* processing the token as
+ // the EOF token may have preceeding comments.
+ if (token.type == TokenType.EOF) {
break;
}
+ token = token.next;
}
}
@@ -114,9 +164,9 @@
}
}
- bool _hasBlankLineBetween(Token first, Token second) {
- final CharacterLocation firstLoc = _lineInfo.getLocation(first.end);
- final CharacterLocation secondLoc = _lineInfo.getLocation(second.offset);
+ bool _hasBlankLineBetween(int offset, int end) {
+ final CharacterLocation firstLoc = _lineInfo.getLocation(offset);
+ final CharacterLocation secondLoc = _lineInfo.getLocation(end);
return secondLoc.lineNumber - firstLoc.lineNumber > 1;
}
@@ -162,15 +212,6 @@
}
@override
- void visitComment(Comment node) {
- if (node.isDocumentation) {
- _computer._addRegion(
- node.offset, node.end, FoldingKind.DOCUMENTATION_COMMENT);
- }
- super.visitComment(node);
- }
-
- @override
void visitConstructorDeclaration(ConstructorDeclaration node) {
_computer._addRegionForAnnotations(node.metadata);
super.visitConstructorDeclaration(node);
@@ -303,3 +344,17 @@
super.visitWhileStatement(node);
}
}
+
+extension _CommentTokenExtensions on Token {
+ static final _newlinePattern = RegExp(r'[\r\n]');
+
+ /// The offset of the first eol character or null
+ /// if no newlines were found.
+ int get eolOffset {
+ final offset = lexeme.indexOf(_newlinePattern);
+ return offset != -1 ? offset : null;
+ }
+
+ /// Whether this comment is a triple-slash single line comment.
+ bool get isTripleSlash => lexeme.startsWith('///');
+}
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index d02da72..fdde79c 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -1055,6 +1055,7 @@
lsp.FoldingRangeKind toFoldingRangeKind(server.FoldingKind kind) {
switch (kind) {
+ case server.FoldingKind.COMMENT:
case server.FoldingKind.DOCUMENTATION_COMMENT:
case server.FoldingKind.FILE_HEADER:
return lsp.FoldingRangeKind.Comment;
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index de9314a..cf6abe5 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -723,6 +723,7 @@
/// ANNOTATIONS
/// BLOCK
/// CLASS_BODY
+/// COMMENT
/// DIRECTIVES
/// DOCUMENTATION_COMMENT
/// FILE_HEADER
@@ -734,6 +735,7 @@
'ANNOTATIONS',
'BLOCK',
'CLASS_BODY',
+ 'COMMENT',
'DIRECTIVES',
'DOCUMENTATION_COMMENT',
'FILE_HEADER',
diff --git a/pkg/analysis_server/test/lsp/folding_test.dart b/pkg/analysis_server/test/lsp/folding_test.dart
index 1caf00a..8b4e389 100644
--- a/pkg/analysis_server/test/lsp/folding_test.dart
+++ b/pkg/analysis_server/test/lsp/folding_test.dart
@@ -44,7 +44,7 @@
Future<void> test_comments() async {
final content = '''
- [[/// This is a comment
+ /// This is a comment[[
/// that spans many lines]]
class MyClass2 {}
''';
@@ -164,13 +164,6 @@
}
Future<void> test_headersImportsComments() async {
- // TODO(dantup): Review why the file header and the method comment ranges
- // are different... one spans only the range to collapse, but the other
- // just starts at the logical block.
- // The LSP spec doesn't give any guidance on whether the first part of
- // the surrounded content should be visible or not after folding
- // so we'll need to revisit this once there's clarification:
- // https://github.com/Microsoft/language-server-protocol/issues/659
final content = '''
// Copyright some year by some people[[
// See LICENCE etc.]]
@@ -178,7 +171,7 @@
import[[ 'dart:io';
import 'dart:async';]]
- [[/// This is not the file header
+ /// This is not the file header[[
/// It's just a comment]]
main() {}
''';
diff --git a/pkg/analysis_server/test/src/computer/folding_computer_test.dart b/pkg/analysis_server/test/src/computer/folding_computer_test.dart
index 0f3fa04..03534e7 100644
--- a/pkg/analysis_server/test/src/computer/folding_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/folding_computer_test.dart
@@ -17,6 +17,12 @@
@reflectiveTest
class FoldingComputerTest extends AbstractContextTest {
+ static const commentKinds = {
+ FoldingKind.FILE_HEADER,
+ FoldingKind.COMMENT,
+ FoldingKind.DOCUMENTATION_COMMENT
+ };
+
String sourcePath;
@override
@@ -125,15 +131,72 @@
Future<void> test_comment_is_not_considered_file_header() async {
var content = """
-// This is not the file header
-// It's just a comment
+// This is not the file header/*1:EXC*/
+// It's just a comment/*1:INC:COMMENT*/
main() {}
""";
// Since there are no region comment markers above
// just check the length instead of the contents
final regions = await _computeRegions(content);
- expect(regions, hasLength(0));
+ _compareRegions(regions, content);
+ }
+
+ Future<void> test_comment_multiline() async {
+ var content = '''
+main() {
+/*/*1:EXC*/
+ * comment 1
+ *//*1:EXC:COMMENT*/
+
+/* this comment starts on the same line as delimeters/*2:EXC*/
+ * second line
+ *//*2:EXC:COMMENT*/
+}
+''';
+
+ final regions = await _computeRegions(content);
+ _compareRegions(regions, content, commentKinds);
+ }
+
+ Future<void> test_comment_singleFollowedByBlankLine() async {
+ var content = '''
+main() {
+// this is/*1:EXC*/
+// a comment/*1:INC:COMMENT*/
+/// this is not part of it
+}
+''';
+
+ final regions = await _computeRegions(content);
+ _compareRegions(regions, content, commentKinds);
+ }
+
+ Future<void> test_comment_singleFollowedByMulti() async {
+ var content = '''
+main() {
+ // this is/*1:EXC*/
+ // a comment/*1:INC:COMMENT*/
+ /* this is not part of it */
+ String foo;
+}
+''';
+
+ final regions = await _computeRegions(content);
+ _compareRegions(regions, content, commentKinds);
+ }
+
+ Future<void> test_comment_singleFollowedByTripleSlash() async {
+ var content = '''
+main() {
+// this is/*1:EXC*/
+// a comment/*1:INC:COMMENT*/
+/// this is not part of it
+}
+''';
+
+ final regions = await _computeRegions(content);
+ _compareRegions(regions, content, commentKinds);
}
Future<void> test_constructor_invocations() async {
@@ -164,7 +227,7 @@
""";
final regions = await _computeRegions(content);
- _compareRegions(regions, content);
+ _compareRegions(regions, content, {FoldingKind.FILE_HEADER});
}
Future<void> test_file_header_does_not_include_block_comments() async {
@@ -179,7 +242,7 @@
""";
final regions = await _computeRegions(content);
- expect(regions, hasLength(0));
+ _compareRegions(regions, content, {FoldingKind.FILE_HEADER});
}
Future<void> test_file_header_with_no_function_comment() async {
@@ -191,7 +254,7 @@
''';
final regions = await _computeRegions(content);
- _compareRegions(regions, content);
+ _compareRegions(regions, content, {FoldingKind.FILE_HEADER});
}
Future<void> test_file_header_with_non_end_of_line_comment() async {
@@ -204,7 +267,7 @@
""";
final regions = await _computeRegions(content);
- _compareRegions(regions, content);
+ _compareRegions(regions, content, {FoldingKind.FILE_HEADER});
}
Future<void> test_file_header_with_script_prefix() async {
@@ -219,6 +282,19 @@
""";
final regions = await _computeRegions(content);
+ _compareRegions(regions, content, {FoldingKind.FILE_HEADER});
+ }
+
+ Future<void> test_fileHeader_singleFollowedByBlank() async {
+ var content = '''
+// this is/*1:EXC*/
+// a file header/*1:INC:FILE_HEADER*/
+
+// this is not part of it
+main() {}
+''';
+
+ final regions = await _computeRegions(content);
_compareRegions(regions, content);
}
@@ -263,7 +339,7 @@
var content = '''
// Content before
-/*1:EXC*//// This is a doc comment
+/// This is a doc comment/*1:EXC*/
/// that spans lines/*1:INC:DOCUMENTATION_COMMENT*/
main() {/*2:INC*/
print("Hello, world!");
@@ -434,11 +510,19 @@
/// Compares provided folding regions with expected
/// regions extracted from the comments in the provided content.
- void _compareRegions(List<FoldingRegion> regions, String content) {
+ ///
+ /// If [onlyKinds] is supplied only regions of that type will be compared.
+ void _compareRegions(List<FoldingRegion> regions, String content,
+ [Set<FoldingKind> onlyKinds]) {
// Find all numeric markers for region starts.
final regex = RegExp(r'/\*(\d+):(INC|EXC)\*/');
final expectedRegions = regex.allMatches(content);
+ if (onlyKinds != null) {
+ regions =
+ regions.where((region) => onlyKinds.contains(region.kind)).toList();
+ }
+
// Check we didn't get more than expected, since the loop below only
// checks for the presence of matches, not absence.
expect(regions, hasLength(expectedRegions.length));
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java b/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java
index be9f538..3b8b236 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java
@@ -21,6 +21,8 @@
public static final String CLASS_BODY = "CLASS_BODY";
+ public static final String COMMENT = "COMMENT";
+
public static final String DIRECTIVES = "DIRECTIVES";
public static final String DOCUMENTATION_COMMENT = "DOCUMENTATION_COMMENT";
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 47b9e8f..7d118ec 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -7,7 +7,7 @@
<body>
<h1>Analysis Server API Specification</h1>
<h1 style="color:#999999">Version
- <version>1.32.1</version>
+ <version>1.32.2</version>
</h1>
<p>
This document contains a specification of the API provided by the
@@ -134,9 +134,13 @@
ignoring the item or treating it with some default/fallback handling.
</p>
<h3>Changelog</h3>
+<h4>1.32.2</h4>
+<ul>
+ <li>Added <tt>FoldingKind.COMMENT</tt> for folding regions for blocks of comments.</li>
+</ul>
<h4>1.32.1</h4>
<ul>
- <li>Added <tt>CompletionSuggestionKind.PACKAGE_NAME</tt> for Pub package name completions in <tt>pubspec.yaml</tt></li>
+ <li>Added <tt>CompletionSuggestionKind.PACKAGE_NAME</tt> for Pub package name completions in <tt>pubspec.yaml</tt>.</li>
</ul>
<h3>Domains</h3>
<p>
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
index 346847c..2b61453 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -1904,6 +1904,7 @@
/// ANNOTATIONS
/// BLOCK
/// CLASS_BODY
+/// COMMENT
/// DIRECTIVES
/// DOCUMENTATION_COMMENT
/// FILE_HEADER
@@ -1920,6 +1921,8 @@
static const FoldingKind CLASS_BODY = FoldingKind._('CLASS_BODY');
+ static const FoldingKind COMMENT = FoldingKind._('COMMENT');
+
static const FoldingKind DIRECTIVES = FoldingKind._('DIRECTIVES');
static const FoldingKind DOCUMENTATION_COMMENT =
@@ -1938,6 +1941,7 @@
ANNOTATIONS,
BLOCK,
CLASS_BODY,
+ COMMENT,
DIRECTIVES,
DOCUMENTATION_COMMENT,
FILE_HEADER,
@@ -1959,6 +1963,8 @@
return BLOCK;
case 'CLASS_BODY':
return CLASS_BODY;
+ case 'COMMENT':
+ return COMMENT;
case 'DIRECTIVES':
return DIRECTIVES;
case 'DOCUMENTATION_COMMENT':
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
index 4ad5e63..68ea10f 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
// To regenerate the file, use the script
// "pkg/analysis_server/tool/spec/generate_files".
-const String PROTOCOL_VERSION = '1.32.1';
+const String PROTOCOL_VERSION = '1.32.2';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index 6aaac51..dd53e3b 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1309,7 +1309,7 @@
An enumeration of the kinds of folding regions.
</p>
- <dl><dt class="value">ANNOTATIONS</dt><dt class="value">BLOCK</dt><dt class="value">CLASS_BODY</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd>
+ <dl><dt class="value">ANNOTATIONS</dt><dt class="value">BLOCK</dt><dt class="value">CLASS_BODY</dt><dt class="value">COMMENT</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd>
<p>
A description of a region that can be folded.
</p>
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 56bcadd..89b8f94 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -1904,6 +1904,7 @@
/// ANNOTATIONS
/// BLOCK
/// CLASS_BODY
+/// COMMENT
/// DIRECTIVES
/// DOCUMENTATION_COMMENT
/// FILE_HEADER
@@ -1920,6 +1921,8 @@
static const FoldingKind CLASS_BODY = FoldingKind._('CLASS_BODY');
+ static const FoldingKind COMMENT = FoldingKind._('COMMENT');
+
static const FoldingKind DIRECTIVES = FoldingKind._('DIRECTIVES');
static const FoldingKind DOCUMENTATION_COMMENT =
@@ -1938,6 +1941,7 @@
ANNOTATIONS,
BLOCK,
CLASS_BODY,
+ COMMENT,
DIRECTIVES,
DOCUMENTATION_COMMENT,
FILE_HEADER,
@@ -1959,6 +1963,8 @@
return BLOCK;
case 'CLASS_BODY':
return CLASS_BODY;
+ case 'COMMENT':
+ return COMMENT;
case 'DIRECTIVES':
return DIRECTIVES;
case 'DOCUMENTATION_COMMENT':
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index d495c4f..3ac543a 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -305,6 +305,7 @@
/// ANNOTATIONS
/// BLOCK
/// CLASS_BODY
+/// COMMENT
/// DIRECTIVES
/// DOCUMENTATION_COMMENT
/// FILE_HEADER
@@ -316,6 +317,7 @@
'ANNOTATIONS',
'BLOCK',
'CLASS_BODY',
+ 'COMMENT',
'DIRECTIVES',
'DOCUMENTATION_COMMENT',
'FILE_HEADER',
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index b1c7056..df7fa1c 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -6,7 +6,7 @@
</head>
<body>
<h1>Common Types</h1>
-<version>1.4.0</version>
+<version>1.4.1</version>
<p>
This document contains a specification of the types that are common between
the analysis server wire protocol and the analysis server plugin wire
@@ -575,6 +575,7 @@
<value><code>ANNOTATIONS</code></value>
<value><code>BLOCK</code></value>
<value><code>CLASS_BODY</code></value>
+ <value><code>COMMENT</code></value>
<value><code>DIRECTIVES</code></value>
<value><code>DOCUMENTATION_COMMENT</code></value>
<value><code>FILE_HEADER</code></value>
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index 0921ddd..44a3ab9 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -17,7 +17,7 @@
const String soundNullSafetyMessage = 'Info: Compiling with sound null safety';
const String unsoundNullSafetyMessage =
- 'Info: Compiling with unsound null safety';
+ 'Info: Compiling without sound null safety';
void defineCompileTests() {
// *** NOTE ***: These tests *must* be run with the `--use-sdk` option
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index 84f9e63..e768713 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -48,7 +48,7 @@
messageCantInferPackagesFromManyInputs,
messageCantInferPackagesFromPackageUri,
messageCompilingWithSoundNullSafety,
- messageCompilingWithUnsoundNullSafety,
+ messageCompilingWithoutSoundNullSafety,
messageInternalProblemProvidedBothCompileSdkAndSdkSummary,
messageMissingInput,
noLength,
@@ -277,8 +277,8 @@
if (_raw.invocationModes.contains(InvocationMode.compile)) {
switch (nnbdMode) {
case NnbdMode.Weak:
- reportWithoutLocation(messageCompilingWithUnsoundNullSafety,
- messageCompilingWithUnsoundNullSafety.severity);
+ reportWithoutLocation(messageCompilingWithoutSoundNullSafety,
+ messageCompilingWithoutSoundNullSafety.severity);
break;
case NnbdMode.Strong:
reportWithoutLocation(messageCompilingWithSoundNullSafety,
diff --git a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
index e962880..b98d56b 100644
--- a/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/constructor_builder.dart
@@ -140,7 +140,7 @@
int charOffset,
this.charOpenParenOffset,
int charEndOffset,
- Constructor referenceFrom,
+ Member referenceFrom,
[String nativeMethodName])
: _constructor = new Constructor(null,
fileUri: compilationUnit.fileUri,
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 7a2103d..c74e46a 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -2258,7 +2258,7 @@
String nativeMethodName,
{Token beginInitializers}) {
MetadataCollector metadataCollector = loader.target.metadataCollector;
- Constructor referenceFrom;
+ Member referenceFrom;
if (_currentClassReferencesFromIndexed != null) {
referenceFrom = _currentClassReferencesFromIndexed.lookupConstructor(
new Name(
@@ -2438,9 +2438,10 @@
procedureName = name;
}
- Reference reference =
- _currentClassReferencesFromIndexed?.lookupGetterReference(new Name(
- procedureName, _currentClassReferencesFromIndexed.library));
+ Reference reference = _currentClassReferencesFromIndexed
+ ?.lookupConstructor(
+ new Name(procedureName, _currentClassReferencesFromIndexed.library))
+ ?.reference;
ProcedureBuilder procedureBuilder;
if (redirectionTarget != null) {
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 794f307..245cd2d 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -70,7 +70,7 @@
ClassInNullAwareReceiver/analyzerCode: Fail
ColonInPlaceOfIn/example: Fail
CompilingWithSoundNullSafety/analyzerCode: Fail
-CompilingWithUnsoundNullSafety/analyzerCode: Fail
+CompilingWithoutSoundNullSafety/analyzerCode: Fail
ConflictingModifiers/part_wrapped_script1: Fail
ConflictingModifiers/script1: Fail
ConflictsWithConstructor/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 5c8b909..c06e19b 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4940,8 +4940,8 @@
script: |
main() {}
-CompilingWithUnsoundNullSafety:
- template: "Compiling with unsound null safety"
+CompilingWithoutSoundNullSafety:
+ template: "Compiling without sound null safety"
configuration: nnbd-weak,compile
severity: INFO
script: |
diff --git a/pkg/front_end/test/crashing_test_case_minimizer_impl.dart b/pkg/front_end/test/crashing_test_case_minimizer_impl.dart
index 6e8344c..abc52b9 100644
--- a/pkg/front_end/test/crashing_test_case_minimizer_impl.dart
+++ b/pkg/front_end/test/crashing_test_case_minimizer_impl.dart
@@ -53,7 +53,7 @@
import 'package:front_end/src/fasta/util/textual_outline.dart'
show textualOutline;
-import 'package:kernel/ast.dart' show Component;
+import 'package:kernel/ast.dart' show Component, LibraryPart;
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
@@ -1746,8 +1746,9 @@
}
bool _isUriNnbd(Uri uri) {
+ Uri asImportUri = _getImportUri(uri);
LibraryBuilder libraryBuilder = _latestCrashingIncrementalCompiler
- .userCode.loader.builders[_getImportUri(uri)];
+ .userCode.loader.builders[asImportUri];
if (libraryBuilder != null) {
return libraryBuilder.isNonNullableByDefault;
}
@@ -1755,12 +1756,18 @@
for (LibraryBuilder libraryBuilder
in _latestCrashingIncrementalCompiler.userCode.loader.builders.values) {
if (libraryBuilder.importUri == uri) {
- print("Found $uri as ${libraryBuilder.importUri} "
- "(!= ${_getImportUri(uri)})");
+ print("Found $uri as ${libraryBuilder.importUri} (!= ${asImportUri})");
return libraryBuilder.isNonNullableByDefault;
}
+ // Check parts too.
+ for (LibraryPart part in libraryBuilder.library.parts) {
+ Uri thisPartUri = libraryBuilder.importUri.resolve(part.partUri);
+ if (thisPartUri == uri || thisPartUri == asImportUri) {
+ print("Found $uri as part of ${libraryBuilder.importUri}");
+ return libraryBuilder.isNonNullableByDefault;
+ }
+ }
}
- // This might be parts?
throw "Couldn't lookup $uri at all!";
}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml
new file mode 100644
index 0000000..7ce01e9f
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml
@@ -0,0 +1,31 @@
+# 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.md file.
+
+# Reproduce a crash.
+
+type: newworld
+trackWidgetCreation: true
+target: VM
+worlds:
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ sources:
+ main.dart: |
+ class Foo {
+ factory Foo.bar() {
+ return null;
+ }
+ void bar() {}
+ }
+ expectedLibraryCount: 1
+
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - main.dart
+ expectedLibraryCount: 1
+ expectsRebuildBodiesOnly: true
+
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml.world.1.expect
new file mode 100644
index 0000000..3416529
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml.world.1.expect
@@ -0,0 +1,20 @@
+main = <No Member>;
+library from "org-dartlang-test:///main.dart" as main {
+
+ class Foo extends dart.core::Object {
+ static factory bar() → main::Foo* {
+ return null;
+ }
+ method bar() → void {}
+ abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → dart.core::bool*; -> dart.core::Object::==
+ abstract member-signature get hashCode() → dart.core::int*; -> dart.core::Object::hashCode
+ abstract member-signature method toString() → dart.core::String*; -> dart.core::Object::toString
+ abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
+ }
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml.world.2.expect
new file mode 100644
index 0000000..3416529
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter.yaml.world.2.expect
@@ -0,0 +1,20 @@
+main = <No Member>;
+library from "org-dartlang-test:///main.dart" as main {
+
+ class Foo extends dart.core::Object {
+ static factory bar() → main::Foo* {
+ return null;
+ }
+ method bar() → void {}
+ abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → dart.core::bool*; -> dart.core::Object::==
+ abstract member-signature get hashCode() → dart.core::int*; -> dart.core::Object::hashCode
+ abstract member-signature method toString() → dart.core::String*; -> dart.core::Object::toString
+ abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
+ }
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml
new file mode 100644
index 0000000..8b31333
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml
@@ -0,0 +1,31 @@
+# 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.md file.
+
+# Reproduce a crash.
+
+type: newworld
+trackWidgetCreation: true
+target: VM
+worlds:
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ sources:
+ main.dart: |
+ class Foo {
+ Foo();
+ factory Foo.bar() = Baz;
+ void bar() {}
+ }
+ class Baz extends Foo {}
+ expectedLibraryCount: 1
+
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - main.dart
+ expectedLibraryCount: 1
+ expectsRebuildBodiesOnly: true
+
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml.world.1.expect
new file mode 100644
index 0000000..47fdc2b
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml.world.1.expect
@@ -0,0 +1,28 @@
+main = <No Member>;
+library from "org-dartlang-test:///main.dart" as main {
+
+ class Foo extends dart.core::Object {
+ static final field dynamic _redirecting# = <dynamic>[main::Foo::bar];
+ constructor •() → main::Foo*
+ : super dart.core::Object::•()
+ ;
+ static factory bar() → main::Foo*
+ let dynamic #redirecting_factory = main::Baz::• in invalid-expression;
+ method bar() → void {}
+ abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → dart.core::bool*; -> dart.core::Object::==
+ abstract member-signature get hashCode() → dart.core::int*; -> dart.core::Object::hashCode
+ abstract member-signature method toString() → dart.core::String*; -> dart.core::Object::toString
+ abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
+ }
+ class Baz extends main::Foo {
+ synthetic constructor •() → main::Baz*
+ : super main::Foo::•()
+ ;
+ }
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml.world.2.expect
new file mode 100644
index 0000000..47fdc2b
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_45_flutter_prime_1.yaml.world.2.expect
@@ -0,0 +1,28 @@
+main = <No Member>;
+library from "org-dartlang-test:///main.dart" as main {
+
+ class Foo extends dart.core::Object {
+ static final field dynamic _redirecting# = <dynamic>[main::Foo::bar];
+ constructor •() → main::Foo*
+ : super dart.core::Object::•()
+ ;
+ static factory bar() → main::Foo*
+ let dynamic #redirecting_factory = main::Baz::• in invalid-expression;
+ method bar() → void {}
+ abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → dart.core::bool*; -> dart.core::Object::==
+ abstract member-signature get hashCode() → dart.core::int*; -> dart.core::Object::hashCode
+ abstract member-signature method toString() → dart.core::String*; -> dart.core::Object::toString
+ abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
+ }
+ class Baz extends main::Foo {
+ synthetic constructor •() → main::Baz*
+ : super main::Foo::•()
+ ;
+ }
+}
diff --git a/pkg/kernel/lib/reference_from_index.dart b/pkg/kernel/lib/reference_from_index.dart
index 0a2ca57..02efc1f 100644
--- a/pkg/kernel/lib/reference_from_index.dart
+++ b/pkg/kernel/lib/reference_from_index.dart
@@ -11,9 +11,11 @@
Extension,
Field,
Library,
+ Member,
Name,
- Reference,
Procedure,
+ ProcedureKind,
+ Reference,
Typedef;
class ReferenceFromIndex {
@@ -41,15 +43,21 @@
void _addProcedures(List<Procedure> procedures) {
for (int i = 0; i < procedures.length; i++) {
- Procedure procedure = procedures[i];
- Name name = procedure.name;
- if (procedure.isSetter) {
- assert(_setterReferences[name] == null);
- _setterReferences[name] = procedure.reference;
- } else {
- assert(_getterReferences[name] == null);
- _getterReferences[name] = procedure.reference;
- }
+ _addProcedure(procedures[i]);
+ }
+ }
+
+ void _addProcedure(Procedure procedure) {
+ Name name = procedure.name;
+ if (procedure.isSetter) {
+ assert(_setterReferences[name] == null);
+ _setterReferences[name] = procedure.reference;
+ } else {
+ assert(_getterReferences[name] == null);
+ assert(procedure.kind == ProcedureKind.Method ||
+ procedure.kind == ProcedureKind.Getter ||
+ procedure.kind == ProcedureKind.Operator);
+ _getterReferences[name] = procedure.reference;
}
}
@@ -104,7 +112,7 @@
}
class IndexedClass extends IndexedContainer {
- final Map<Name, Constructor> _constructors = new Map<Name, Constructor>();
+ final Map<Name, Member> _constructors = new Map<Name, Member>();
final Library library;
IndexedClass._(Class c, this.library) {
@@ -112,9 +120,16 @@
Constructor constructor = c.constructors[i];
_constructors[constructor.name] = constructor;
}
- _addProcedures(c.procedures);
+ for (int i = 0; i < c.procedures.length; i++) {
+ Procedure procedure = c.procedures[i];
+ if (procedure.isFactory) {
+ _constructors[procedure.name] = procedure;
+ } else {
+ _addProcedure(procedure);
+ }
+ }
_addFields(c.fields);
}
- Constructor lookupConstructor(Name name) => _constructors[name];
+ Member lookupConstructor(Name name) => _constructors[name];
}
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 2dc17ea..2213ef3 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -231,6 +231,10 @@
final Map<MethodInvocation, DecoratedType Function(DecoratedType)>
_deferredMethodInvocationProcessing = {};
+ /// If we are visiting a local function or closure, the set of local variables
+ /// assigned to so far inside it. Otherwise `null`.
+ Set<Element> _elementsWrittenToInLocalFunction;
+
EdgeBuilder(this.typeProvider, this._typeSystem, this._variables, this._graph,
this.source, this.listener, this._decoratedClassHierarchy,
{this.instrumentation})
@@ -857,12 +861,21 @@
if (_flowAnalysis != null) {
// This is a local function.
var previousPostDominatedLocals = _postDominatedLocals;
+ var previousElementsWrittenToInLocalFunction =
+ _elementsWrittenToInLocalFunction;
try {
+ _elementsWrittenToInLocalFunction = {};
_postDominatedLocals = _ScopedLocalSet();
_flowAnalysis.functionExpression_begin(node);
_dispatch(node.functionExpression);
_flowAnalysis.functionExpression_end();
} finally {
+ for (var element in _elementsWrittenToInLocalFunction) {
+ previousElementsWrittenToInLocalFunction?.add(element);
+ previousPostDominatedLocals.removeFromAllScopes(element);
+ }
+ _elementsWrittenToInLocalFunction =
+ previousElementsWrittenToInLocalFunction;
_postDominatedLocals = previousPostDominatedLocals;
}
} else {
@@ -911,7 +924,12 @@
_currentFunctionType =
_variables.decoratedElementType(node.declaredElement);
var previousPostDominatedLocals = _postDominatedLocals;
+ var previousElementsWrittenToInLocalFunction =
+ _elementsWrittenToInLocalFunction;
try {
+ if (node.parent is! FunctionDeclaration) {
+ _elementsWrittenToInLocalFunction = {};
+ }
_postDominatedLocals = _ScopedLocalSet();
_postDominatedLocals.doScoped(
elements: node.declaredElement.parameters,
@@ -921,6 +939,12 @@
} finally {
if (node.parent is! FunctionDeclaration) {
_flowAnalysis.functionExpression_end();
+ for (var element in _elementsWrittenToInLocalFunction) {
+ previousElementsWrittenToInLocalFunction?.add(element);
+ previousPostDominatedLocals.removeFromAllScopes(element);
+ }
+ _elementsWrittenToInLocalFunction =
+ previousElementsWrittenToInLocalFunction;
}
_currentFunctionType = previousFunctionType;
_currentFunctionExpression = previousFunction;
@@ -2373,7 +2397,11 @@
}
}
if (destinationExpression != null) {
- _postDominatedLocals.removeReferenceFromAllScopes(destinationExpression);
+ var element = _postDominatedLocals
+ .removeReferenceFromAllScopes(destinationExpression);
+ if (element != null) {
+ _elementsWrittenToInLocalFunction?.add(element);
+ }
}
return sourceType;
}
@@ -3639,11 +3667,16 @@
return false;
}
- void removeReferenceFromAllScopes(Expression expression) {
+ /// If [expression] references an element, removes that element from all
+ /// scopes and returns it. Otherwise returns `null`.
+ Element removeReferenceFromAllScopes(Expression expression) {
expression = expression.unParenthesized;
if (expression is SimpleIdentifier) {
var element = expression.staticElement;
removeFromAllScopes(element);
+ return element;
+ } else {
+ return null;
}
}
}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 4006512..2ff78bf 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -4591,6 +4591,80 @@
await _checkSingleFileChanges(content, expected);
}
+ Future<void> test_local_function_expression_inhibits_non_null_intent() async {
+ var content = '''
+void call(void Function() callback) {
+ callback();
+}
+test(int i, int j) {
+ call(() {
+ i = j;
+ });
+ print(i + 1);
+}
+main() {
+ test(null, 0);
+}
+''';
+ // `print(i + 1)` does *not* demonstrate non-null intent for `i` because it
+ // is write captured by the local function expression, so it's not
+ // guaranteed that a null value of `i` on entry to the function will lead to
+ // an exception.
+ var expected = '''
+void call(void Function() callback) {
+ callback();
+}
+test(int? i, int j) {
+ call(() {
+ i = j;
+ });
+ print(i! + 1);
+}
+main() {
+ test(null, 0);
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_local_function_inhibits_non_null_intent() async {
+ var content = '''
+void call(void Function() callback) {
+ callback();
+}
+test(int i, int j) {
+ void f() {
+ i = j;
+ }
+ call(f);
+ print(i + 1);
+}
+main() {
+ test(null, 0);
+}
+''';
+ // `print(i + 1)` does *not* demonstrate non-null intent for `i` because it
+ // is write captured by the local function expression, so it's not
+ // guaranteed that a null value of `i` on entry to the function will lead to
+ // an exception.
+ var expected = '''
+void call(void Function() callback) {
+ callback();
+}
+test(int? i, int j) {
+ void f() {
+ i = j;
+ }
+ call(f);
+ print(i! + 1);
+}
+main() {
+ test(null, 0);
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
Future<void> test_local_function_return() async {
var content = '''
void test({String foo}) async {
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 1885167..e943de9 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -6197,6 +6197,58 @@
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
+ Future<void> test_postDominators_subFunction_nested() async {
+ await analyze('''
+f1(int a, int b, int c, int d) {
+ f2() {
+ a = 0;
+ f3() {
+ b = 0;
+ }
+ c = 0;
+ }
+ a + 1;
+ b + 1;
+ c + 1;
+ d + 1;
+}
+''');
+ // a, b, and c may all be written to prior to their use, so their use sites
+ // don't demonstrate non-null intent.
+ assertEdge(decoratedTypeAnnotation('int a').node, never, hard: false);
+ assertEdge(decoratedTypeAnnotation('int b').node, never, hard: false);
+ assertEdge(decoratedTypeAnnotation('int c').node, never, hard: false);
+ // However, the use of `d` does demonstrate non-null intent because there's
+ // no write to `d`.
+ assertEdge(decoratedTypeAnnotation('int d').node, never, hard: true);
+ }
+
+ Future<void> test_postDominators_subFunction_nested_closure() async {
+ await analyze('''
+f1(int a, int b, int c, int d) {
+ var f2 = () {
+ a = 0;
+ var f3 = () {
+ b = 0;
+ };
+ c = 0;
+ };
+ a + 1;
+ b + 1;
+ c + 1;
+ d + 1;
+}
+''');
+ // a, b, and c may all be written to prior to their use, so their use sites
+ // don't demonstrate non-null intent.
+ assertEdge(decoratedTypeAnnotation('int a').node, never, hard: false);
+ assertEdge(decoratedTypeAnnotation('int b').node, never, hard: false);
+ assertEdge(decoratedTypeAnnotation('int c').node, never, hard: false);
+ // However, the use of `d` does demonstrate non-null intent because there's
+ // no write to `d`.
+ assertEdge(decoratedTypeAnnotation('int d').node, never, hard: true);
+ }
+
Future<void> test_postDominators_ternaryOperator() async {
await analyze('''
class C {
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
index e5a757f..185b116 100644
--- a/sdk/lib/io/secure_socket.dart
+++ b/sdk/lib/io/secure_socket.dart
@@ -882,69 +882,70 @@
}
}
- Future<void> _scheduleFilter() async {
+ Future<void> _scheduleFilter() {
_filterPending = true;
return _tryFilter();
}
Future<void> _tryFilter() async {
- if (_status == closedStatus) {
- return;
- }
- if (!_filterPending || _filterActive) {
- return;
- }
- _filterActive = true;
- _filterPending = false;
-
try {
- _filterStatus = await _pushAllFilterStages();
- _filterActive = false;
- if (_status == closedStatus) {
- _secureFilter!.destroy();
- _secureFilter = null;
- return;
- }
- _socket.readEventsEnabled = true;
- if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) {
- // Checks for and handles all cases of partially closed sockets.
- shutdown(SocketDirection.send);
+ while (true) {
if (_status == closedStatus) {
return;
}
- }
- if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) {
- if (_status == handshakeStatus) {
- _secureFilter!.handshake();
- if (_status == handshakeStatus) {
- throw new HandshakeException(
- 'Connection terminated during handshake');
+ if (!_filterPending || _filterActive) {
+ return;
+ }
+ _filterActive = true;
+ _filterPending = false;
+
+ _filterStatus = await _pushAllFilterStages();
+ _filterActive = false;
+ if (_status == closedStatus) {
+ _secureFilter!.destroy();
+ _secureFilter = null;
+ return;
+ }
+ _socket.readEventsEnabled = true;
+ if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) {
+ // Checks for and handles all cases of partially closed sockets.
+ shutdown(SocketDirection.send);
+ if (_status == closedStatus) {
+ return;
}
}
- _closeHandler();
- }
- if (_status == closedStatus) {
- return;
- }
- if (_filterStatus.progress) {
- _filterPending = true;
- if (_filterStatus.writeEncryptedNoLongerEmpty) {
- _writeSocket();
+ if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) {
+ if (_status == handshakeStatus) {
+ _secureFilter!.handshake();
+ if (_status == handshakeStatus) {
+ throw new HandshakeException(
+ 'Connection terminated during handshake');
+ }
+ }
+ _closeHandler();
}
- if (_filterStatus.writePlaintextNoLongerFull) {
- _sendWriteEvent();
+ if (_status == closedStatus) {
+ return;
}
- if (_filterStatus.readEncryptedNoLongerFull) {
- _readSocket();
- }
- if (_filterStatus.readPlaintextNoLongerEmpty) {
- _scheduleReadEvent();
- }
- if (_status == handshakeStatus) {
- await _secureHandshake();
+ if (_filterStatus.progress) {
+ _filterPending = true;
+ if (_filterStatus.writeEncryptedNoLongerEmpty) {
+ _writeSocket();
+ }
+ if (_filterStatus.writePlaintextNoLongerFull) {
+ _sendWriteEvent();
+ }
+ if (_filterStatus.readEncryptedNoLongerFull) {
+ _readSocket();
+ }
+ if (_filterStatus.readPlaintextNoLongerEmpty) {
+ _scheduleReadEvent();
+ }
+ if (_status == handshakeStatus) {
+ await _secureHandshake();
+ }
}
}
- return _tryFilter();
} catch (e, st) {
_reportError(e, st);
}
diff --git a/tools/VERSION b/tools/VERSION
index 0ee1a68..3e628b6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 245
+PRERELEASE 246
PRERELEASE_PATCH 0
\ No newline at end of file