Version 2.17.0-255.0.dev
Merge commit '06e0672dc778361b3ee632f33933f22ffdaf0d9f' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart b/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart
index c2dbee3..b044921 100644
--- a/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/util/dependency_walker.dart
@@ -29,6 +29,8 @@
// the walk, restore the state of the [Node] data structures so
// that further evaluation will be safe.
+ if (startingPoint.isEvaluated) return;
+
// The index which will be assigned to the next node that is
// freshly visited.
int index = 1;
diff --git a/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart b/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart
index 5ca34a6..ec5cd44 100644
--- a/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart
+++ b/pkg/_fe_analyzer_shared/test/util/dependency_walker_test.dart
@@ -169,6 +169,19 @@
],
[false, false]);
});
+
+ test('Do not revisit already-evaluated nodes', () {
+ makeGraph({
+ 'a': ['b'],
+ 'b': []
+ });
+ var walker = TestWalker();
+ var a = getNode('a');
+ walker.walk(a);
+ expect(walker._evaluations, hasLength(2));
+ walker.walk(a);
+ expect(walker._evaluations, hasLength(2));
+ });
}
class TestNode extends Node<TestNode> {
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index e65f669..d610cb4 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.10
+ 1.33.0
</h1>
<p>
This document contains a specification of the API provided by the
@@ -236,6 +236,11 @@
ignoring the item or treating it with some default/fallback handling.
</p>
<h3>Changelog</h3>
+<h4>1.33.0</h4>
+<ul>
+ <li>Requests <tt>getSuggestions2</tt> and <tt>getSuggestionDetails2</tt>
+ are enabled.</li>
+</ul>
<h4>1.32.10</h4>
<ul>
<li>The <tt>MOVE_FILE</tt> refactor now supports moving/renaming folders.</li>
@@ -313,9 +318,11 @@
</ul>
<p><a href="#domain_completion">Completion</a></p><ul><li><a href="#request_completion.getSuggestions">completion.getSuggestions</a></li>
+<li><a href="#request_completion.getSuggestions2">completion.getSuggestions2</a></li>
<li><a href="#request_completion.setSubscriptions">completion.setSubscriptions</a></li>
<li><a class="deprecated" href="#request_completion.registerLibraryPaths">completion.registerLibraryPaths</a></li>
<li><a href="#request_completion.getSuggestionDetails">completion.getSuggestionDetails</a></li>
+<li><a href="#request_completion.getSuggestionDetails2">completion.getSuggestionDetails2</a></li>
</ul>
<p><a href="#domain_search">Search</a></p><ul><li><a href="#request_search.findElementReferences">search.findElementReferences</a></li>
@@ -1606,6 +1613,94 @@
The identifier used to associate results with this
completion request.
</p>
+ </dd></dl></dd><dt class="request"><a name="request_completion.getSuggestions2">completion.getSuggestions2</a></dt><dd><div class="box"><pre>request: {
+ "id": String
+ "method": "completion.getSuggestions2"
+ "params": {
+ "<b>file</b>": <a href="#type_FilePath">FilePath</a>
+ "<b>offset</b>": int
+ "<b>maxResults</b>": int
+ "<b>completionCaseMatchingMode</b>": <span style="color:#999999">optional</span> <a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a>
+ }
+}</pre><br><pre>response: {
+ "id": String
+ "error": <span style="color:#999999">optional</span> <a href="#type_RequestError">RequestError</a>
+ "result": {
+ "<b>replacementOffset</b>": int
+ "<b>replacementLength</b>": int
+ "<b>suggestions</b>": List<<a href="#type_CompletionSuggestion">CompletionSuggestion</a>>
+ "<b>isIncomplete</b>": bool
+ }
+}</pre></div>
+ <p>
+ Request that completion suggestions for the given offset in the given
+ file be returned. The suggestions will be filtered using fuzzy matching
+ with the already existing prefix.
+ </p>
+
+
+ <h4>parameters:</h4><dl><dt class="field"><b>file: <a href="#type_FilePath">FilePath</a></b></dt><dd>
+
+ <p>
+ The file containing the point at which suggestions are to be made.
+ </p>
+ </dd><dt class="field"><b>offset: int</b></dt><dd>
+
+ <p>
+ The offset within the file at which suggestions are to be made.
+ </p>
+ </dd><dt class="field"><b>maxResults: int</b></dt><dd>
+
+ <p>
+ The maximum number of suggestions to return. If the number of
+ suggestions after filtering is greater than the <tt>maxResults</tt>,
+ then <tt>isIncomplete</tt> is set to <tt>true</tt>.
+ </p>
+ </dd><dt class="field"><b>completionCaseMatchingMode: <a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a><span style="color:#999999"> (optional)</span></b></dt><dd>
+
+ <p>
+ The mode of code completion being invoked. If no value is provided,
+ <tt>MATCH_FIRST_CHAR</tt> will be assumed.
+ </p>
+ </dd></dl><h4>returns:</h4><dl><dt class="field"><b>replacementOffset: int</b></dt><dd>
+
+ <p>
+ The offset of the start of the text to be replaced. This will be
+ different from the offset used to request the completion suggestions
+ if there was a portion of an identifier before the original offset.
+ In particular, the replacementOffset will be the offset of the
+ beginning of said identifier.
+ </p>
+ </dd><dt class="field"><b>replacementLength: int</b></dt><dd>
+
+ <p>
+ The length of the text to be replaced if the remainder of the
+ identifier containing the cursor is to be replaced when the
+ suggestion is applied (that is, the number of characters in the
+ existing identifier).
+ </p>
+ </dd><dt class="field"><b>suggestions: List<<a href="#type_CompletionSuggestion">CompletionSuggestion</a>></b></dt><dd>
+
+ <p>
+ The completion suggestions being reported. This list is filtered
+ by the already existing prefix, and sorted first by relevance,
+ and (if the same) by the suggestion text. The list will have at
+ most <tt>maxResults</tt> items. If the user types a new keystroke,
+ the client is expected to either do local filtering (when the
+ returned list was complete), or ask the server again (if
+ <tt>isIncomplete</tt> was <tt>true</tt>).
+ </p>
+ <p>
+ This list contains suggestions from both imported, and not yet
+ imported libraries. Items from not yet imported libraries will
+ have <tt>isNotImported</tt> set to <tt>true</tt>.
+ </p>
+ </dd><dt class="field"><b>isIncomplete: bool</b></dt><dd>
+
+ <p>
+ True if the number of suggestions after filtering was greater than
+ the requested <tt>maxResults</tt>.
+ </p>
</dd></dl></dd><dt class="request"><a name="request_completion.setSubscriptions">completion.setSubscriptions</a></dt><dd><div class="box"><pre>request: {
"id": String
"method": "completion.setSubscriptions"
@@ -1717,6 +1812,73 @@
the accepted completion suggestion needs to be imported. The field
will be omitted if there are no additional changes that need to be made.
</p>
+ </dd></dl></dd><dt class="request"><a name="request_completion.getSuggestionDetails2">completion.getSuggestionDetails2</a></dt><dd><div class="box"><pre>request: {
+ "id": String
+ "method": "completion.getSuggestionDetails2"
+ "params": {
+ "<b>file</b>": <a href="#type_FilePath">FilePath</a>
+ "<b>offset</b>": int
+ "<b>completion</b>": String
+ "<b>libraryUri</b>": String
+ }
+}</pre><br><pre>response: {
+ "id": String
+ "error": <span style="color:#999999">optional</span> <a href="#type_RequestError">RequestError</a>
+ "result": {
+ "<b>completion</b>": String
+ "<b>change</b>": <a href="#type_SourceChange">SourceChange</a>
+ }
+}</pre></div>
+ <p>
+ Clients must make this request when the user has selected a completion
+ suggestion with the <tt>isNotImported</tt> field set to <tt>true</tt>.
+ The server will respond with the text to insert, as well as any
+ <tt>SourceChange</tt> that needs to be applied in case the completion
+ requires an additional import to be added. The text to insert might be
+ different from the original suggestion to include an import prefix if the
+ library will be imported with a prefix to avoid shadowing
+ conflicts in the file.
+ </p>
+
+
+ <h4>parameters:</h4><dl><dt class="field"><b>file: <a href="#type_FilePath">FilePath</a></b></dt><dd>
+
+ <p>
+ The path of the file into which this completion is being inserted.
+ </p>
+ </dd><dt class="field"><b>offset: int</b></dt><dd>
+
+ <p>
+ The offset in the file where the completion will be inserted.
+ </p>
+ </dd><dt class="field"><b>completion: String</b></dt><dd>
+
+ <p>
+ The <tt>completion</tt> from the selected
+ <tt>CompletionSuggestion</tt>. It could be a name of a class, or a
+ name of a constructor in form "typeName.constructorName()", or an
+ enumeration constant in form "enumName.constantName", etc.
+ </p>
+ </dd><dt class="field"><b>libraryUri: String</b></dt><dd>
+
+ <p>
+ The URI of the library to import, so that the element referenced
+ in the <tt>completion</tt> becomes accessible.
+ </p>
+ </dd></dl><h4>returns:</h4><dl><dt class="field"><b>completion: String</b></dt><dd>
+
+ <p>
+ The full text to insert, which possibly includes now an import prefix.
+ The client should insert this text, not the <tt>completion</tt> from
+ the selected <tt>CompletionSuggestion</tt>.
+ </p>
+ </dd><dt class="field"><b>change: <a href="#type_SourceChange">SourceChange</a></b></dt><dd>
+
+ <p>
+ A change for the client to apply to make the accepted completion
+ suggestion available. In most cases the change is to add a new
+ import directive to the file.
+ </p>
</dd></dl></dd></dl><h3>Notifications</h3><dl><dt class="notification"><a name="notification_completion.results">completion.results</a></dt><dd><div class="box"><pre>notification: {
"event": "completion.results"
"params": {
@@ -3712,6 +3874,37 @@
The type of the options parameter being suggested. This field is
omitted if the parameterName field is omitted.
</p>
+ </dd><dt class="field"><b>libraryUri: String<span style="color:#999999"> (optional)</span></b></dt><dd>
+
+ <p>
+ This field is omitted if <tt>getSuggestions</tt> was used rather
+ than <tt>getSuggestions2</tt>.
+ </p>
+ <p>
+ This field is omitted if this suggestion corresponds to a locally
+ declared element.
+ </p>
+ <p>
+ If this suggestion corresponds to an already imported element,
+ then this field is the URI of a library that provides this element,
+ not the URI of the library where the element is declared.
+ </p>
+ <p>
+ If this suggestion corresponds to an element from a not yet
+ imported library, this field is the URI of a library that could be
+ imported to make this suggestion accessible in the file where
+ completion was requested, such as <tt>package:foo/bar.dart</tt> or
+ <tt>file:///home/me/workspace/foo/test/bar_test.dart</tt>.
+ </p>
+ </dd><dt class="field"><b>isNotImported: bool<span style="color:#999999"> (optional)</span></b></dt><dd>
+
+ <p>
+ True if the suggestion is for an element from a not yet imported
+ library. This field is omitted if the element is declared locally,
+ or is from library is already imported, so that the suggestion can
+ be inserted as is, or if <tt>getSuggestions</tt> was used rather
+ than <tt>getSuggestions2</tt>.
+ </p>
</dd></dl></dd><dt class="typeDefinition"><a name="type_CompletionSuggestionKind">CompletionSuggestionKind: String</a></dt><dd>
<p>
An enumeration of the kinds of elements that can be included in a
@@ -6156,7 +6349,7 @@
TODO: TBD
</p>
<h2 class="domain"><a name="index">Index</a></h2>
-<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li><li><a href="#request_server.cancelRequest">cancelRequest</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.log">log</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li><li><a href="#request_completion.setSubscriptions">setSubscriptions</a></li><li><a href="#request_completion.registerLibraryPaths">registerLibraryPaths</a></li><li><a href="#request_completion.getSuggestionDetails">getSuggestionDetails</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li><li><a href="#notification_completion.availableSuggestions">availableSuggestions</a></li><li><a href="#notification_completion.existingImports">existingImports</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getPostfixCompletion">getPostfixCompletion</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.getSuggestions">getSuggestions</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h4>flutter (<a href="#domain_flutter">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_flutter.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_flutter.outline">outline</a></li></ul></div></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_AvailableSuggestion">AvailableSuggestion</a></li><li><a href="#type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_AvailableSuggestionSet">AvailableSuggestionSet</a></li><li><a href="#type_BulkFix">BulkFix</a></li><li><a href="#type_BulkFixDetail">BulkFixDetail</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionMode">CompletionMode</a></li><li><a href="#type_CompletionService">CompletionService</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_DiagnosticMessage">DiagnosticMessage</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_ExistingImport">ExistingImport</a></li><li><a href="#type_ExistingImports">ExistingImports</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FlutterOutline">FlutterOutline</a></li><li><a href="#type_FlutterOutlineAttribute">FlutterOutlineAttribute</a></li><li><a href="#type_FlutterOutlineKind">FlutterOutlineKind</a></li><li><a href="#type_FlutterService">FlutterService</a></li><li><a href="#type_FlutterWidgetProperty">FlutterWidgetProperty</a></li><li><a href="#type_FlutterWidgetPropertyEditor">FlutterWidgetPropertyEditor</a></li><li><a href="#type_FlutterWidgetPropertyEditorKind">FlutterWidgetPropertyEditorKind</a></li><li><a href="#type_FlutterWidgetPropertyValue">FlutterWidgetPropertyValue</a></li><li><a href="#type_FlutterWidgetPropertyValueEnumItem">FlutterWidgetPropertyValueEnumItem</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElementSet">ImportedElementSet</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a></li><li><a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LibraryPathSet">LibraryPathSet</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_RuntimeCompletionExpression">RuntimeCompletionExpression</a></li><li><a href="#type_RuntimeCompletionExpressionType">RuntimeCompletionExpressionType</a></li><li><a href="#type_RuntimeCompletionExpressionTypeKind">RuntimeCompletionExpressionTypeKind</a></li><li><a href="#type_RuntimeCompletionVariable">RuntimeCompletionVariable</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_EXTRACT_WIDGET">EXTRACT_WIDGET</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
+<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li><li><a href="#request_server.cancelRequest">cancelRequest</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.log">log</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li><li><a href="#request_completion.getSuggestions2">getSuggestions2</a></li><li><a href="#request_completion.setSubscriptions">setSubscriptions</a></li><li><a href="#request_completion.registerLibraryPaths">registerLibraryPaths</a></li><li><a href="#request_completion.getSuggestionDetails">getSuggestionDetails</a></li><li><a href="#request_completion.getSuggestionDetails2">getSuggestionDetails2</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li><li><a href="#notification_completion.availableSuggestions">availableSuggestions</a></li><li><a href="#notification_completion.existingImports">existingImports</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getPostfixCompletion">getPostfixCompletion</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.getSuggestions">getSuggestions</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h4>flutter (<a href="#domain_flutter">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_flutter.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_flutter.outline">outline</a></li></ul></div></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_AvailableSuggestion">AvailableSuggestion</a></li><li><a href="#type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_AvailableSuggestionSet">AvailableSuggestionSet</a></li><li><a href="#type_BulkFix">BulkFix</a></li><li><a href="#type_BulkFixDetail">BulkFixDetail</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionCaseMatchingMode">CompletionCaseMatchingMode</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionMode">CompletionMode</a></li><li><a href="#type_CompletionService">CompletionService</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_DiagnosticMessage">DiagnosticMessage</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_ExistingImport">ExistingImport</a></li><li><a href="#type_ExistingImports">ExistingImports</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FlutterOutline">FlutterOutline</a></li><li><a href="#type_FlutterOutlineAttribute">FlutterOutlineAttribute</a></li><li><a href="#type_FlutterOutlineKind">FlutterOutlineKind</a></li><li><a href="#type_FlutterService">FlutterService</a></li><li><a href="#type_FlutterWidgetProperty">FlutterWidgetProperty</a></li><li><a href="#type_FlutterWidgetPropertyEditor">FlutterWidgetPropertyEditor</a></li><li><a href="#type_FlutterWidgetPropertyEditorKind">FlutterWidgetPropertyEditorKind</a></li><li><a href="#type_FlutterWidgetPropertyValue">FlutterWidgetPropertyValue</a></li><li><a href="#type_FlutterWidgetPropertyValueEnumItem">FlutterWidgetPropertyValueEnumItem</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElementSet">ImportedElementSet</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a></li><li><a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LibraryPathSet">LibraryPathSet</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_RuntimeCompletionExpression">RuntimeCompletionExpression</a></li><li><a href="#type_RuntimeCompletionExpressionType">RuntimeCompletionExpressionType</a></li><li><a href="#type_RuntimeCompletionExpressionTypeKind">RuntimeCompletionExpressionTypeKind</a></li><li><a href="#type_RuntimeCompletionVariable">RuntimeCompletionVariable</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_EXTRACT_WIDGET">EXTRACT_WIDGET</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
</body></html>
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
index 65c9513..6d5b0d8 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
@@ -65,10 +65,10 @@
final T1? _t1;
final T2? _t2;
- Either2.t1(T1 this._t1)
+ const Either2.t1(T1 this._t1)
: _t2 = null,
_which = 1;
- Either2.t2(T2 this._t2)
+ const Either2.t2(T2 this._t2)
: _t1 = null,
_which = 2;
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index 0a14c4f..24ad02a 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.10';
+const String PROTOCOL_VERSION = '1.33.0';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
index 34e97b2..84ef2e3 100644
--- a/pkg/analysis_server/lib/src/cider/rename.dart
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -39,6 +39,8 @@
status = validateFunctionName(name);
} else if (element is FieldElement) {
status = validateFieldName(name);
+ } else if (element is MethodElement) {
+ status = validateMethodName(name);
} else if (element is TypeAliasElement) {
status = validateTypeAliasName(name);
} else if (element is ClassElement) {
diff --git a/pkg/analysis_server/lib/src/lsp/client_capabilities.dart b/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
index bac248e..9a50687 100644
--- a/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
@@ -68,6 +68,7 @@
final bool literalCodeActions;
final bool insertReplaceCompletionRanges;
final bool definitionLocationLink;
+ final bool typeDefinitionLocationLink;
final bool hierarchicalSymbols;
final bool diagnosticCodeDescription;
final Set<CodeActionKind> codeActionKinds;
@@ -109,6 +110,8 @@
false,
definitionLocationLink =
raw.textDocument?.definition?.linkSupport ?? false,
+ typeDefinitionLocationLink =
+ raw.textDocument?.typeDefinition?.linkSupport ?? false,
completionItemTags = _listToSet(
raw.textDocument?.completion?.completionItem?.tagSupport?.valueSet),
diagnosticTags = _listToSet(
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
index ee853d6..7e3ecad 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
@@ -40,8 +40,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
final lineInfo = unit.lineInfo;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 6ae02e1..acff954 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -75,8 +75,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
final supportsApplyEdit = clientCapabilities.applyEdit;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index f64f323..f385bd1 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -55,8 +55,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
final triggerCharacter = params.context?.triggerCharacter;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index cd60079..ef497f6 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -198,8 +198,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
final file = data.file;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index f65aa56..1383b80 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -76,8 +76,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
final supportsLocationLink = clientCapabilities.definitionLocationLink;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
index 25a78ca..cb0344b 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
@@ -32,8 +32,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
// If triggered automatically by pressing the trigger character, we will
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
index abc53cf..ceb4204 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
@@ -36,6 +36,7 @@
import 'package:analysis_server/src/lsp/handlers/handler_shutdown.dart';
import 'package:analysis_server/src/lsp/handlers/handler_signature_help.dart';
import 'package:analysis_server/src/lsp/handlers/handler_text_document_changes.dart';
+import 'package:analysis_server/src/lsp/handlers/handler_type_definition.dart';
import 'package:analysis_server/src/lsp/handlers/handler_will_rename_files.dart';
import 'package:analysis_server/src/lsp/handlers/handler_workspace_configuration.dart';
import 'package:analysis_server/src/lsp/handlers/handler_workspace_symbols.dart';
@@ -82,6 +83,7 @@
registerHandler(DocumentColorPresentationHandler(server));
registerHandler(SignatureHelpHandler(server));
registerHandler(DefinitionHandler(server));
+ registerHandler(TypeDefinitionHandler(server));
registerHandler(SuperHandler(server));
registerHandler(ReferencesHandler(server));
registerHandler(ImplementationHandler(server));
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
new file mode 100644
index 0000000..f32c4c1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_type_definition.dart
@@ -0,0 +1,166 @@
+// Copyright (c) 2022, 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/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dart/element/element.dart' show ElementImpl;
+import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
+import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
+
+typedef _LocationsOrLinks = Either2<List<Location>, List<LocationLink>>;
+
+class TypeDefinitionHandler
+ extends MessageHandler<TypeDefinitionParams, _LocationsOrLinks>
+ with LspPluginRequestHandlerMixin {
+ static const _emptyResult = _LocationsOrLinks.t1([]);
+
+ TypeDefinitionHandler(LspAnalysisServer server) : super(server);
+
+ @override
+ Method get handlesMessage => Method.textDocument_typeDefinition;
+
+ @override
+ LspJsonHandler<TypeDefinitionParams> get jsonHandler =>
+ TypeDefinitionParams.jsonHandler;
+
+ @override
+ Future<ErrorOr<_LocationsOrLinks>> handle(
+ TypeDefinitionParams params, CancellationToken token) async {
+ if (!isDartDocument(params.textDocument)) {
+ return success(_emptyResult);
+ }
+
+ final clientCapabilities = server.clientCapabilities;
+ if (clientCapabilities == null) {
+ // This should not happen unless a client misbehaves.
+ return serverNotInitializedError;
+ }
+
+ /// Whether the client supports `LocationLink` results instead of the
+ /// original `Location`. `LocationLink`s can include an additional `Range`
+ /// to distinguish between codeRange and nameRange (selectionRange), and
+ /// also an `originSelectionRange` that tells the client which range the
+ /// result is valid for.
+ final supportsLocationLink = clientCapabilities.typeDefinitionLocationLink;
+ final pos = params.position;
+ final path = pathOfDoc(params.textDocument);
+
+ return path.mapResult((path) async {
+ final result = await server.getResolvedUnit(path);
+ if (result == null) {
+ return success(_emptyResult);
+ }
+
+ final offset = toOffset(result.lineInfo, pos);
+ return offset.mapResult((offset) async {
+ final node = NodeLocator(offset).searchWithin(result.unit);
+ if (node == null) {
+ return success(_emptyResult);
+ }
+
+ final type = node is Expression ? _getType(node) : null;
+ final element = type?.element;
+ if (element is! ElementImpl) {
+ return success(_emptyResult);
+ }
+
+ // Obtain a `LineInfo` for the targets file to map offsets.
+ final targetUnitElement =
+ element.thisOrAncestorOfType<CompilationUnitElement>();
+ final targetLineInfo = targetUnitElement?.lineInfo;
+ if (targetLineInfo == null) {
+ return success(_emptyResult);
+ }
+
+ final converter = AnalyzerConverter();
+ final location = converter.locationFromElement(element);
+ if (location == null) {
+ return success(_emptyResult);
+ }
+
+ if (supportsLocationLink) {
+ return success(_LocationsOrLinks.t2([
+ _toLocationLink(
+ result.lineInfo, targetLineInfo, node, element, location)
+ ]));
+ } else {
+ return success(
+ _LocationsOrLinks.t1([_toLocation(location, targetLineInfo)]));
+ }
+ });
+ });
+ }
+
+ /// Creates an LSP [Location] for the server [location].
+ Location _toLocation(plugin.Location location, LineInfo lineInfo) {
+ return Location(
+ uri: Uri.file(location.file).toString(),
+ range: toRange(lineInfo, location.offset, location.length),
+ );
+ }
+
+ /// Creates an LSP [LocationLink] for the server [targetLocation].
+ ///
+ /// Uses [originLineInfo] and [originNode] to compute `originSelectionRange`
+ /// and [targetLineInfo] and [targetElement] for code ranges.
+ LocationLink _toLocationLink(
+ LineInfo originLineInfo,
+ LineInfo targetLineInfo,
+ AstNode originNode,
+ ElementImpl targetElement,
+ plugin.Location targetLocation,
+ ) {
+ final nameRange =
+ toRange(targetLineInfo, targetLocation.offset, targetLocation.length);
+
+ final codeOffset = targetElement.codeOffset;
+ final codeLength = targetElement.codeLength;
+ final codeRange = codeOffset != null && codeLength != null
+ ? toRange(targetLineInfo, codeOffset, codeLength)
+ : nameRange;
+
+ return LocationLink(
+ originSelectionRange:
+ toRange(originLineInfo, originNode.offset, originNode.length),
+ targetUri: Uri.file(targetLocation.file).toString(),
+ targetRange: codeRange,
+ targetSelectionRange: nameRange,
+ );
+ }
+
+ /// Returns the [DartType] most appropriate for navigating to from [node] when
+ /// invoking Go to Type Definition.
+ static DartType? _getType(Expression node) {
+ if (node is SimpleIdentifier) {
+ final element = node.staticElement;
+ if (element is ClassElement) {
+ return element.thisType;
+ } else if (element is VariableElement) {
+ if (node.inDeclarationContext()) {
+ return element.type;
+ }
+ final parent = node.parent?.parent;
+ if (parent is NamedExpression && parent.name.label == node) {
+ return element.type;
+ }
+ } else if (node.inSetterContext()) {
+ final writeElement = node.writeElement;
+ if (writeElement is PropertyAccessorElement) {
+ return writeElement.variable.type;
+ }
+ }
+ }
+
+ return node.staticType;
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
index 435013c..31ea25d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
@@ -25,8 +25,7 @@
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
- return error(ErrorCodes.ServerNotInitialized,
- 'Requests not before server is initilized');
+ return serverNotInitializedError;
}
// Respond to empty queries with an empty list. The spec says this should
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
index 0e1c738..9d3225d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
@@ -44,6 +44,9 @@
final fileModifiedError = error<R>(ErrorCodes.ContentModified,
'Document was modified before operation completed', null);
+ final serverNotInitializedError = error<R>(ErrorCodes.ServerNotInitialized,
+ 'Request not valid before server is initialized');
+
LspAnalysisServer get server;
bool fileHasBeenModified(String path, num? clientVersion) {
diff --git a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
index 0b28c2c..9146784 100644
--- a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
+++ b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
@@ -39,6 +39,7 @@
Method.textDocument_rename,
Method.textDocument_foldingRange,
Method.textDocument_selectionRange,
+ Method.textDocument_typeDefinition,
// workspace.fileOperations covers all file operation methods but we only
// support this one.
Method.workspace_willRenameFiles,
@@ -109,6 +110,9 @@
bool get textSync =>
_capabilities.textDocument?.synchronization?.dynamicRegistration ?? false;
+ bool get typeDefinition =>
+ _capabilities.textDocument?.typeDefinition?.dynamicRegistration ?? false;
+
bool get typeFormatting =>
_capabilities.textDocument?.onTypeFormatting?.dynamicRegistration ??
false;
@@ -450,6 +454,13 @@
TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
);
register(
+ dynamicRegistrations.typeDefinition,
+ Method.textDocument_typeDefinition,
+ TextDocumentRegistrationOptions(
+ documentSelector: [dartFiles], // This one is currently Dart-specific
+ ),
+ );
+ register(
dynamicRegistrations.implementation,
Method.textDocument_implementation,
TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
index c66c747..31d1539 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
@@ -34,6 +34,13 @@
if (body is! BlockFunctionBody || body.isGenerator) {
return;
}
+ if (body.keyword?.precedingComments != null ||
+ body.block.leftBracket.precedingComments != null ||
+ body.block.rightBracket.precedingComments != null) {
+ // TODO(https://github.com/dart-lang/sdk/issues/29313): Include comments
+ // in fixed output.
+ return;
+ }
var parent = body.parent;
if (parent is ConstructorDeclaration && parent.factoryKeyword == null) {
return;
@@ -48,8 +55,31 @@
Expression? returnExpression;
if (onlyStatement is ReturnStatement) {
returnExpression = onlyStatement.expression;
+ if (onlyStatement.returnKeyword.precedingComments != null) {
+ // TODO(https://github.com/dart-lang/sdk/issues/29313): Include comments
+ // in fixed output.
+ return;
+ }
+ // TODO(https://github.com/dart-lang/sdk/issues/29313): If there are
+ // comments after `return` keyword, before the expression, either return
+ // without offering a fix, or include the comments in the fixed output.
+
+ if (onlyStatement.semicolon.precedingComments != null) {
+ // TODO(https://github.com/dart-lang/sdk/issues/29313): Include
+ // comments in fixed output.
+ return;
+ }
} else if (onlyStatement is ExpressionStatement) {
returnExpression = onlyStatement.expression;
+ // TODO(https://github.com/dart-lang/sdk/issues/29313): If there are
+ // comments before the expression, either return without offering a fix,
+ // or include the comments in the fixed output.
+
+ if (onlyStatement.semicolon?.precedingComments != null) {
+ // TODO(https://github.com/dart-lang/sdk/issues/29313): Include comments
+ // in fixed output.
+ return;
+ }
}
if (returnExpression == null) {
return;
diff --git a/pkg/analysis_server/test/analysis/get_navigation_test.dart b/pkg/analysis_server/test/analysis/get_navigation_test.dart
index 2a70304..9927cea 100644
--- a/pkg/analysis_server/test/analysis/get_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/get_navigation_test.dart
@@ -4,12 +4,11 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
-import 'package:analysis_server/src/domain_analysis.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../mocks.dart';
import 'notification_navigation_test.dart';
void main() {
@@ -25,10 +24,7 @@
@override
Future<void> setUp() async {
super.setUp();
- server.handlers = [
- AnalysisDomainHandler(server),
- ];
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_beforeAnalysisComplete() async {
@@ -38,7 +34,7 @@
print(test);
}
''');
- await _getNavigation(testFile, testCode.indexOf('test);'), 0);
+ await _getNavigation(search: 'test);');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
@@ -49,8 +45,7 @@
String main() {
}''');
await waitForTasksFinished();
- var search = 'Returns';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: 'Returns', length: 1);
expect(regions, hasLength(0));
}
@@ -60,15 +55,14 @@
String main() {
}''');
await waitForTasksFinished();
- var search = '[String';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: '[String', length: 1);
expect(regions, hasLength(1));
assertHasRegion('String]');
}
Future<void> test_comment_toolSeeCodeComment() async {
var examplePath = 'examples/api/foo.dart';
- newFile2(convertPath('$testFolder/$examplePath'), '');
+ newFile2('$testPackageLibPath/$examplePath', '');
addTestFile('''
/// {@tool dartpad}
/// ** See code in $examplePath **
@@ -76,7 +70,7 @@
String main() {
}''');
await waitForTasksFinished();
- await _getNavigation(testFile, testCode.indexOf(examplePath), 1);
+ await _getNavigation(search: examplePath, length: 1);
expect(regions, hasLength(1));
assertHasRegion(examplePath, examplePath.length);
}
@@ -95,26 +89,26 @@
}
final a = Foo();
-final b = new Foo.named();
+final b = new Foo.named(); // 0
''');
await waitForTasksFinished();
// Without `new` / unnamed
- await _getNavigation(testFile, testCode.indexOf('Foo();'), 0);
+ await _getNavigation(search: 'Foo();');
expect(regions, hasLength(1));
expect(regions.first.targets, hasLength(1));
var target = targets[regions.first.targets.first];
expect(target.kind, ElementKind.CONSTRUCTOR);
- expect(target.offset, testCode.indexOf('Foo() {'));
+ expect(target.offset, findOffset('Foo() {'));
expect(target.length, 3);
// With `new` / named
- await _getNavigation(testFile, testCode.indexOf('new Foo.named();') + 8, 0);
+ await _getNavigation(search: 'named(); // 0');
expect(regions, hasLength(1));
expect(regions.first.targets, hasLength(1));
target = targets[regions.first.targets.first];
expect(target.kind, ElementKind.CONSTRUCTOR);
- expect(target.offset, testCode.indexOf('named() {'));
+ expect(target.offset, findOffset('named() {'));
expect(target.length, 5);
}
@@ -129,7 +123,7 @@
}
''';
addTestFile(text);
- await _getNavigation(testFile, text.indexOf('Foo foo'), 0);
+ await _getNavigation(search: 'Foo foo');
expect(targets, hasLength(1));
var target = targets.first;
expect(target.kind, ElementKind.CLASS);
@@ -140,7 +134,7 @@
}
Future<void> test_fileDoesNotExist() async {
- var file = convertPath('$projectPath/doesNotExist.dart');
+ var file = convertPath('$testPackageLibPath/doesNotExist.dart');
var request = _createGetNavigationRequest(file, 0, 100);
var response = await serverChannel.sendRequest(request);
expect(response.error, isNull);
@@ -150,15 +144,16 @@
expect(result['regions'], isEmpty);
}
+ /// TODO(scheglov) Rewrite these tests to work with any file.
+ @FailingTest(reason: 'requires infrastructure rewriting')
Future<void> test_fileOutsideOfRoot() async {
- testFile = convertPath('/outside.dart');
- addTestFile('''
+ var file = newFile2('/outside.dart', '''
main() {
var test = 0;
print(test);
}
''');
- await _getNavigation(testFile, testCode.indexOf('test);'), 0);
+ await _getNavigation(file: file, search: 'test);');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
@@ -170,7 +165,7 @@
main() {
}''');
await waitForTasksFinished();
- await _getNavigation(testFile, 0, 17);
+ await _getNavigation(offset: 0, length: 17);
expect(regions, hasLength(1));
assertHasRegionString("'dart:math'");
expect(testTargets, hasLength(1));
@@ -184,7 +179,7 @@
main() {
}''');
await waitForTasksFinished();
- await _getNavigation(testFile, 7, 11);
+ await _getNavigation(offset: 7, length: 11);
expect(regions, hasLength(1));
assertHasRegionString("'dart:math'");
expect(testTargets, hasLength(1));
@@ -192,8 +187,8 @@
}
Future<void> test_importUri_configurations() async {
- final ioFile = newFile2(join(testFolder, 'io.dart'), '');
- final htmlFile = newFile2(join(testFolder, 'html.dart'), '');
+ final ioFile = newFile2('$testPackageLibPath/io.dart', '');
+ final htmlFile = newFile2('$testPackageLibPath/html.dart', '');
addTestFile('''
import 'foo.dart'
if (dart.library.io) 'io.dart'
@@ -204,7 +199,7 @@
await waitForTasksFinished();
// Request navigations for 'io.dart'
- await _getNavigation(testFile, 41, 9);
+ await _getNavigation(offset: 41, length: 9);
expect(regions, hasLength(1));
assertHasRegionString("'io.dart'");
expect(testTargets, hasLength(1));
@@ -213,7 +208,7 @@
expect(targetFiles[target.fileIndex], equals(ioFile.path));
// Request navigations for 'html.dart'
- await _getNavigation(testFile, 76, 11);
+ await _getNavigation(offset: 76, length: 11);
expect(regions, hasLength(1));
assertHasRegionString("'html.dart'");
expect(testTargets, hasLength(1));
@@ -224,20 +219,22 @@
Future<void> test_invalidFilePathFormat_notAbsolute() async {
var request = _createGetNavigationRequest('test.dart', 0, 0);
- var response = await waitResponse(request);
- expect(
+ var response = await handleRequest(request);
+ assertResponseFailure(
response,
- isResponseFailure(requestId, RequestErrorCode.INVALID_FILE_PATH_FORMAT),
+ requestId: requestId,
+ errorCode: RequestErrorCode.INVALID_FILE_PATH_FORMAT,
);
}
Future<void> test_invalidFilePathFormat_notNormalized() async {
var request =
_createGetNavigationRequest(convertPath('/foo/../bar/test.dart'), 0, 0);
- var response = await waitResponse(request);
- expect(
+ var response = await handleRequest(request);
+ assertResponseFailure(
response,
- isResponseFailure(requestId, RequestErrorCode.INVALID_FILE_PATH_FORMAT),
+ requestId: requestId,
+ errorCode: RequestErrorCode.INVALID_FILE_PATH_FORMAT,
);
}
@@ -254,7 +251,7 @@
await waitForTasksFinished();
// request navigation
var navCode = ' + bbb + ';
- await _getNavigation(testFile, testCode.indexOf(navCode), navCode.length);
+ await _getNavigation(search: navCode, length: navCode.length);
// verify
{
assertHasRegion('aaa +');
@@ -287,32 +284,32 @@
await waitForTasksFinished();
{
var search = '[0';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[](index)', 2);
}
{
var search = ']; // []';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[](index)', 2);
}
{
var search = '[1';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '] = 1';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '[2';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '] += 2';
- await _getNavigation(testFile, testCode.indexOf(search), 1);
+ await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
}
@@ -325,7 +322,7 @@
}
''');
await waitForTasksFinished();
- await _getNavigation(testFile, testCode.indexOf(');'), 0);
+ await _getNavigation(search: ');');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
@@ -338,7 +335,7 @@
}
''');
await waitForTasksFinished();
- await _getNavigation(testFile, testCode.indexOf('test);'), 0);
+ await _getNavigation(search: 'test);');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
@@ -348,8 +345,23 @@
.toRequest(requestId);
}
- Future<void> _getNavigation(String file, int offset, int length) async {
- var request = _createGetNavigationRequest(file, offset, length);
+ Future<void> _getNavigation({
+ File? file,
+ int? offset,
+ String? search,
+ int length = 0,
+ }) async {
+ file ??= testFile;
+
+ if (offset == null) {
+ if (search != null) {
+ offset = offsetInFile(file, search);
+ } else {
+ throw ArgumentError("Either 'offset' or 'search' must be provided");
+ }
+ }
+
+ var request = _createGetNavigationRequest(file.path, offset, length);
var response = await serverChannel.sendRequest(request);
var result = AnalysisGetNavigationResult.fromResponse(response);
targetFiles = result.files;
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index 71a606a..e869127 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -12,6 +12,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../analysis_abstract.dart';
+import '../analysis_server_base.dart';
void main() {
defineReflectiveSuite(() {
@@ -19,7 +20,7 @@
});
}
-class AbstractNavigationTest extends AbstractAnalysisTest {
+class AbstractNavigationTest extends PubPackageAnalysisServerTest {
late List<NavigationRegion> regions;
late List<NavigationTarget> targets;
late List<String> targetFiles;
@@ -89,7 +90,7 @@
if (length == -1) {
length = findIdentifierLength(search);
}
- assertHasFileTarget(testFile, offset, length);
+ assertHasFileTarget(testFile.path, offset, length);
}
/// TODO(scheglov) Improve target matching.
@@ -179,7 +180,12 @@
final Completer<void> _resultsAvailable = Completer();
Future<void> prepareNavigation() async {
- await addAnalysisSubscription(AnalysisService.NAVIGATION, testFile);
+ await handleSuccessfulRequest(
+ AnalysisSetSubscriptionsParams({
+ AnalysisService.NAVIGATION: [testFile.path],
+ }).toRequest('0'),
+ );
+
await _resultsAvailable.future;
assertRegionsSorted();
}
@@ -188,7 +194,7 @@
void processNotification(Notification notification) {
if (notification.event == ANALYSIS_NOTIFICATION_NAVIGATION) {
var params = AnalysisNavigationParams.fromNotification(notification);
- if (params.file == testFile) {
+ if (params.file == testFile.path) {
regions = params.regions;
targets = params.targets;
targetFiles = params.files;
@@ -200,7 +206,7 @@
@override
Future<void> setUp() async {
super.setUp();
- await createProject();
+ await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_afterAnalysis() async {
@@ -208,7 +214,7 @@
class AAA {}
AAA aaa;
''');
- await waitForTasksFinished();
+ await server.onAnalysisComplete;
await prepareNavigation();
assertHasRegionTarget('AAA aaa;', 'AAA {}');
}
@@ -274,7 +280,7 @@
}
Future<void> test_annotationConstructor_importPrefix() async {
- newFile2(join(testFolder, 'my_annotation.dart'), r'''
+ newFile2('$testPackageLibPath/my_annotation.dart', r'''
library an;
class MyAnnotation {
const MyAnnotation();
@@ -349,7 +355,7 @@
}
Future<void> test_annotationField_importPrefix() async {
- newFile2(join(testFolder, 'mayn.dart'), r'''
+ newFile2('$testPackageLibPath/mayn.dart', r'''
library an;
const myan = new Object();
''');
@@ -821,7 +827,7 @@
}
Future<void> test_functionReference_importPrefix_function() async {
- newFile2(join(testFolder, 'a.dart'), r'''
+ newFile2('$testPackageLibPath/a.dart', r'''
void foo<T>() {}
''');
addTestFile('''
@@ -1070,8 +1076,8 @@
}
Future<void> test_multiplyDefinedElement() async {
- newFile2('$projectPath/bin/libA.dart', 'library A; int TEST = 1;');
- newFile2('$projectPath/bin/libB.dart', 'library B; int TEST = 2;');
+ newFile2('$testPackageLibPath/libA.dart', 'library A; int TEST = 1;');
+ newFile2('$testPackageLibPath/libB.dart', 'library B; int TEST = 2;');
addTestFile('''
import 'libA.dart';
import 'libB.dart';
@@ -1162,7 +1168,7 @@
Future<void> test_partOf() async {
var libCode = 'library lib; part "test.dart";';
- var libFile = newFile2('$projectPath/bin/lib.dart', libCode).path;
+ var libFile = newFile2('$testPackageLibPath/lib.dart', libCode).path;
addTestFile('part of lib;');
await prepareNavigation();
assertHasRegionString('lib');
@@ -1221,9 +1227,10 @@
}
Future<void> test_string_configuration() async {
- newFile2('$projectPath/bin/lib.dart', '').path;
- var lib2File = newFile2('$projectPath/bin/lib2.dart', '').path;
- addTestFile('import "lib.dart" if (dart.library.html) "lib2.dart";');
+ newFile2('$testPackageLibPath/lib.dart', '').path;
+ var lib2File = newFile2('$testPackageLibPath/lib2.dart', '').path;
+ newFile2(
+ testFilePath, 'import "lib.dart" if (dart.library.html) "lib2.dart";');
await prepareNavigation();
assertHasRegionString('"lib2.dart"');
assertHasFileTarget(lib2File, 0, 0);
@@ -1231,7 +1238,7 @@
Future<void> test_string_export() async {
var libCode = 'library lib;';
- var libFile = newFile2('$projectPath/bin/lib.dart', libCode).path;
+ var libFile = newFile2('$testPackageLibPath/lib.dart', libCode).path;
addTestFile('export "lib.dart";');
await prepareNavigation();
assertHasRegionString('"lib.dart"');
@@ -1246,7 +1253,7 @@
Future<void> test_string_import() async {
var libCode = 'library lib;';
- var libFile = newFile2('$projectPath/bin/lib.dart', libCode).path;
+ var libFile = newFile2('$testPackageLibPath/lib.dart', libCode).path;
addTestFile('import "lib.dart";');
await prepareNavigation();
assertHasRegionString('"lib.dart"');
@@ -1267,7 +1274,8 @@
Future<void> test_string_part() async {
var unitCode = 'part of lib; f() {}';
- var unitFile = newFile2('$projectPath/bin/test_unit.dart', unitCode).path;
+ var unitFile =
+ newFile2('$testPackageLibPath/test_unit.dart', unitCode).path;
addTestFile('''
library lib;
part "test_unit.dart";
diff --git a/pkg/analysis_server/test/analysis_server_base.dart b/pkg/analysis_server/test/analysis_server_base.dart
index 8dbec0e..a6d5579 100644
--- a/pkg/analysis_server/test/analysis_server_base.dart
+++ b/pkg/analysis_server/test/analysis_server_base.dart
@@ -102,6 +102,11 @@
String get workspaceRootPath => '/home';
+ /// TODO(scheglov) rename
+ void addTestFile(String content) {
+ newFile2(testFilePath, content);
+ }
+
void assertResponseFailure(
Response response, {
required String requestId,
@@ -125,9 +130,7 @@
/// Fails if not found.
/// TODO(scheglov) Rename it.
int findOffset(String search) {
- var offset = testFileContent.indexOf(search);
- expect(offset, isNot(-1));
- return offset;
+ return offsetInFile(testFile, search);
}
Future<Response> handleRequest(Request request) async {
@@ -141,6 +144,15 @@
return response;
}
+ /// Returns the offset of [search] in [file].
+ /// Fails if not found.
+ int offsetInFile(File file, String search) {
+ var content = file.readAsStringSync();
+ var offset = content.indexOf(search);
+ expect(offset, isNot(-1));
+ return offset;
+ }
+
void processNotification(Notification notification) {}
Future<void> setRoots({
@@ -195,6 +207,12 @@
await server.dispose();
}
+ /// Returns a [Future] that completes when the server's analysis is complete.
+ Future<void> waitForTasksFinished() async {
+ await pumpEventQueue(times: 1 << 10);
+ await server.onAnalysisComplete;
+ }
+
void writePackageConfig(Folder root, PackageConfigFileBuilder config) {
newPackageConfigJsonFile(
root.path,
diff --git a/pkg/analysis_server/test/client/completion_driver_test.dart b/pkg/analysis_server/test/client/completion_driver_test.dart
index 7090fc1..c8ee38b 100644
--- a/pkg/analysis_server/test/client/completion_driver_test.dart
+++ b/pkg/analysis_server/test/client/completion_driver_test.dart
@@ -39,6 +39,7 @@
AnalysisServerOptions get serverOptions => AnalysisServerOptions();
+ @override
Future<List<CompletionSuggestion>> addTestFile(String content,
{int? offset}) async {
driver.addTestFile(content, offset: offset);
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 84aeaf6..c1c7fcc 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -288,6 +288,8 @@
/// "hasNamedParameters": optional bool
/// "parameterName": optional String
/// "parameterType": optional String
+/// "libraryUri": optional String
+/// "isNotImported": optional bool
/// }
final Matcher isCompletionSuggestion =
LazyMatcher(() => MatchesJsonObject('CompletionSuggestion', {
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index c74663d..fd2d8b2 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -304,6 +304,7 @@
formats: [],
tokenModifiers: [],
tokenTypes: []).toJson(),
+ 'typeDefinition': {'dynamicRegistration': true},
});
}
@@ -510,6 +511,7 @@
) {
return extendTextDocumentCapabilities(source, {
'definition': {'linkSupport': true},
+ 'typeDefinition': {'linkSupport': true},
'implementation': {'linkSupport': true}
});
}
@@ -1294,6 +1296,43 @@
return expectSuccessfulResponseTo(request, Location.fromJson);
}
+ Future<Either2<List<Location>, List<LocationLink>>> getTypeDefinition(
+ Uri uri, Position pos) {
+ final request = makeRequest(
+ Method.textDocument_typeDefinition,
+ TypeDefinitionParams(
+ textDocument: TextDocumentIdentifier(uri: uri.toString()),
+ position: pos,
+ ),
+ );
+ return expectSuccessfulResponseTo(
+ request,
+ _generateFromJsonFor(
+ _canParseList(Location.canParse),
+ _fromJsonList(Location.fromJson),
+ _canParseList(LocationLink.canParse),
+ _fromJsonList(LocationLink.fromJson)),
+ );
+ }
+
+ Future<List<Location>> getTypeDefinitionAsLocation(
+ Uri uri, Position pos) async {
+ final results = await getTypeDefinition(uri, pos);
+ return results.map(
+ (locations) => locations,
+ (locationLinks) => throw 'Expected List<Location> got List<LocationLink>',
+ );
+ }
+
+ Future<List<LocationLink>> getTypeDefinitionAsLocationLinks(
+ Uri uri, Position pos) async {
+ final results = await getTypeDefinition(uri, pos);
+ return results.map(
+ (locations) => throw 'Expected List<LocationLink> got List<Location>',
+ (locationLinks) => locationLinks,
+ );
+ }
+
Future<List<SymbolInformation>> getWorkspaceSymbols(String query) {
final request = makeRequest(
Method.workspace_symbol,
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index 5e5c5f5..c563746 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -43,6 +43,7 @@
import 'signature_help_test.dart' as signature_help;
import 'snippets_test.dart' as snippets;
import 'super_test.dart' as get_super;
+import 'type_definition_test.dart' as type_definition;
import 'will_rename_files_test.dart' as will_rename_files;
import 'workspace_symbols_test.dart' as workspace_symbols;
@@ -87,6 +88,7 @@
server.main();
signature_help.main();
snippets.main();
+ type_definition.main();
will_rename_files.main();
workspace_symbols.main();
}, name: 'lsp');
diff --git a/pkg/analysis_server/test/lsp/type_definition_test.dart b/pkg/analysis_server/test/lsp/type_definition_test.dart
new file mode 100644
index 0000000..9f003e8
--- /dev/null
+++ b/pkg/analysis_server/test/lsp/type_definition_test.dart
@@ -0,0 +1,299 @@
+// Copyright (c) 2022, 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/lsp_protocol/protocol_generated.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'server_abstract.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(TypeDefinitionTest);
+ });
+}
+
+@reflectiveTest
+class TypeDefinitionTest extends AbstractLspAnalysisServerTest {
+ Future<void> test_currentFile() async {
+ final contents = '''
+class [[A]] {}
+
+final [[a^]] = A();
+''';
+
+ final ranges = rangesFromMarkers(contents);
+ final targetRange = ranges[0];
+ final originRange = ranges[1];
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, originRange);
+ expect(result.targetUri, mainFileUri.toString());
+ expect(result.targetSelectionRange, targetRange);
+ expect(result.targetRange, rangeOfString(contents, 'class A {}'));
+ }
+
+ Future<void> test_doubleLiteral() async {
+ final contents = '''
+const a = [[12^.3]];
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'double');
+ }
+
+ Future<void> test_getter() async {
+ final contents = '''
+class A {
+ String get aaa => '';
+}
+
+void f() {
+ final a = A();
+ print(a.[[a^aa]]);
+}
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_intLiteral() async {
+ final contents = '''
+const a = [[12^3]];
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'int');
+ }
+
+ /// Checks a result when the client does not support [LocationLink], only
+ /// the original LSP [Location].
+ Future<void> test_location() async {
+ final contents = '''
+const a^ = 'test string';
+''';
+
+ final result = await _getLocationResult(contents);
+ expect(result.uri, 'file:///sdk/lib/core/core.dart');
+ _expectNameRange(result.range, 'String');
+ }
+
+ Future<void> test_nonDartFile() async {
+ final contents = '''
+const a = '^';
+''';
+
+ newFile2(pubspecFilePath, withoutMarkers(contents));
+ await initialize();
+ final results = await getTypeDefinitionAsLocation(
+ mainFileUri, positionFromMarker(contents));
+ expect(results, isEmpty);
+ }
+
+ Future<void> test_otherFile() async {
+ final otherFilePath = join(projectFolderPath, 'lib', 'other.dart');
+ final otherFileUri = Uri.file(otherFilePath);
+ final contents = '''
+import 'other.dart';
+
+final [[a^]] = A();
+''';
+
+ final otherContents = '''
+class [[A]] {}
+''';
+
+ newFile2(otherFilePath, withoutMarkers(otherContents));
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ expect(result.targetUri, otherFileUri.toString());
+ expect(result.targetSelectionRange, rangeFromMarkers(otherContents));
+ expect(result.targetRange, rangeOfString(otherContents, 'class A {}'));
+ }
+
+ Future<void> test_parameter() async {
+ final contents = '''
+void f(String a) {
+ void f([['te^st']]);
+}
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_parameterName() async {
+ final contents = '''
+void f({String a}) {
+ void f([[a^]]: 'test');
+}
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_setter() async {
+ final contents = '''
+class A {
+ set aaa(String value) {}
+}
+
+void f() {
+ final a = A();
+ a.[[a^aa]] = '';
+}
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_stringLiteral() async {
+ final contents = '''
+const a = [['te^st string']];
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_type() async {
+ final contents = '''
+[[St^ring]] a = '';
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_unopenedFile() async {
+ final contents = '''
+const a = [['^']];
+''';
+
+ newFile2(mainFilePath, withoutMarkers(contents));
+ final result = await _getResult(contents, inOpenFile: false);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_variableDeclaration() async {
+ final contents = '''
+const [[a^]] = 'test string';
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_variableDeclaration_inferredType() async {
+ final contents = '''
+var [[a^]] = 'test string';
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_variableReference() async {
+ final contents = '''
+void f() {
+ const a = 'test string';
+ print([[a^]]);
+}
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ Future<void> test_variableReference_inferredType() async {
+ final contents = '''
+void f() {
+ var a = 'test string';
+ print([[a^]]);
+}
+''';
+
+ final result = await _getResult(contents);
+ expect(result.originSelectionRange, rangeFromMarkers(contents));
+ _expectSdkCoreType(result, 'String');
+ }
+
+ /// Expects [range] looks consistent with a range of an elements code.
+ ///
+ /// This is used for SDK sources where the exact location is not known to the
+ /// test.
+ void _expectCodeRange(Range range) {
+ expect(range.start.line, isPositive);
+ expect(range.end.line, isPositive);
+ // And a range that spans multiple lines.
+ expect(range.start.line, lessThan(range.end.line));
+ }
+
+ /// Expects [range] looks consistent with a range of an elements name.
+ ///
+ /// This is used for SDK sources where the exact location is not known to the
+ /// test.
+ void _expectNameRange(Range range, String name) {
+ expect(range.start.line, isPositive);
+ expect(range.end.line, isPositive);
+ // Expect a single line, with the length matching `name`.
+ expect(range.start.line, range.end.line);
+ expect(
+ range.end.character - range.start.character,
+ name.length,
+ );
+ }
+
+ /// Expects [range] looks consistent with a range of an elements code.
+ ///
+ /// This is used for SDK sources where the exact location is not known to the
+ /// test.
+ void _expectSdkCoreType(LocationLink result, String typeName) {
+ expect(result.targetUri, 'file:///sdk/lib/core/core.dart');
+ _expectNameRange(result.targetSelectionRange, typeName);
+ _expectCodeRange(result.targetRange);
+ }
+
+ /// Gets the type definition as an LSP Location object.
+ Future<Location> _getLocationResult(String contents) async {
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(contents));
+ final results = await getTypeDefinitionAsLocation(
+ mainFileUri, positionFromMarker(contents));
+ return results.single;
+ }
+
+ /// Advertises support for the LSP LocationLink type and gets the type
+ /// definition using that.
+ Future<LocationLink> _getResult(String contents,
+ {Uri? fileUri, bool inOpenFile = true}) async {
+ fileUri ??= mainFileUri;
+ await initialize(
+ textDocumentCapabilities:
+ withLocationLinkSupport(emptyTextDocumentClientCapabilities),
+ );
+ if (inOpenFile) {
+ await openFile(fileUri, withoutMarkers(contents));
+ }
+ final results = await getTypeDefinitionAsLocationLinks(
+ mainFileUri,
+ positionFromMarker(contents),
+ );
+ return results.single;
+ }
+}
diff --git a/pkg/analysis_server/test/src/cider/rename_test.dart b/pkg/analysis_server/test/src/cider/rename_test.dart
index 0147ec9..ac912a4 100644
--- a/pkg/analysis_server/test/src/cider/rename_test.dart
+++ b/pkg/analysis_server/test/src/cider/rename_test.dart
@@ -359,6 +359,29 @@
[CharacterLocation(2, 7), CharacterLocation(2, 22)]));
}
+ void test_rename_method_imported() async {
+ var a = newFile2('/workspace/dart/test/lib/a.dart', r'''
+class A {
+ foo() {}
+}
+''');
+ await fileResolver.resolve2(path: a.path);
+ var result = await _rename(r'''
+import 'a.dart';
+void f() {
+ var a = A().^foo();
+}
+''', 'bar');
+ expect(result!.matches.length, 2);
+ expect(result.matches, [
+ CiderSearchMatch(convertPath('/workspace/dart/test/lib/a.dart'), [
+ CharacterLocation(2, 3),
+ ]),
+ CiderSearchMatch(convertPath('/workspace/dart/test/lib/test.dart'),
+ [CharacterLocation(3, 15)])
+ ]);
+ }
+
void test_rename_parameter() async {
var result = await _rename(r'''
void foo(String ^a) {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart
index fc5175e..10ba918 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart
@@ -73,6 +73,96 @@
''');
}
+ Future<void> test_closure_hasBlockComment_afterReturnStatement() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() {
+ return 42;
+ // Comment.
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
+ Future<void> test_closure_hasBlockComment_beforeReturnKeyword() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() {
+ // Comment.
+ return 42;
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
+ Future<void> test_closure_hasBlockComment_multiple() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() {
+ // Comment.
+
+ // Comment 2.
+ return 42;
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
+ Future<void> test_closure_hasInlineComment_beforeBodyKeyword() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() /* Comment. */ async {
+ return 42;
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
+ Future<void> test_closure_hasInlineComment_beforeOpenBrace() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() /* Comment. */ {
+ return 42;
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
+ Future<void> test_closure_hasInlineComment_beforeReturn() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() {
+ /* Comment. */
+ return 42;
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
+ Future<void> test_closure_hasInlineComment_beforeReturnSemicolon() async {
+ await resolveTestCode('''
+setup(x) {}
+main() {
+ setup(() {
+ return 42 /* Comment. */;
+ });
+}
+''');
+ await assertNoAssistAt('return');
+ }
+
Future<void> test_closure_voidExpression() async {
await resolveTestCode('''
setup(x) {}
@@ -156,12 +246,12 @@
Future<void> test_method_onBlock() async {
await resolveTestCode('''
class A {
- m() { // marker
+ m() {
return 42;
}
}
''');
- await assertHasAssistAt('{ // marker', '''
+ await assertHasAssistAt('m() {', '''
class A {
m() => 42;
}
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index 25a61e3..a27f04c 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -86,7 +86,7 @@
| textDocument/signatureHelp | ✅ | ✅ | | ✅ | ✅ | trigger character handling outstanding
| textDocument/declaration | | | | | |
| textDocument/definition | ✅ | ✅ | ✅ | ✅ | ✅ |
-| textDocument/typeDefinition | | | | | |
+| textDocument/typeDefinition | ✅ | ✅ | | ✅ | ✅ |
| textDocument/implementation | ✅ | ✅ | | ✅ | ✅ |
| textDocument/references | ✅ | ✅ | | ✅ | ✅ |
| textDocument/documentHighlight | ✅ | ✅ | | ✅ | ✅ |
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index d1dbf063..aaaeeb5 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.10</version>
+ <version>1.33.0</version>
</h1>
<p>
This document contains a specification of the API provided by the
@@ -134,6 +134,11 @@
ignoring the item or treating it with some default/fallback handling.
</p>
<h3>Changelog</h3>
+<h4>1.33.0</h4>
+<ul>
+ <li>Requests <tt>getSuggestions2</tt> and <tt>getSuggestionDetails2</tt>
+ are enabled.</li>
+</ul>
<h4>1.32.10</h4>
<ul>
<li>The <tt>MOVE_FILE</tt> refactor now supports moving/renaming folders.</li>
@@ -1485,7 +1490,7 @@
</field>
</result>
</request>
- <request method="getSuggestions2" experimental="true">
+ <request method="getSuggestions2">
<p>
Request that completion suggestions for the given offset in the given
file be returned. The suggestions will be filtered using fuzzy matching
@@ -1690,7 +1695,7 @@
</field>
</result>
</request>
- <request method="getSuggestionDetails2" experimental="true">
+ <request method="getSuggestionDetails2">
<p>
Clients must make this request when the user has selected a completion
suggestion with the <tt>isNotImported</tt> field set to <tt>true</tt>.
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 97e7427..e129eed 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -504,6 +504,8 @@
/// "hasNamedParameters": optional bool
/// "parameterName": optional String
/// "parameterType": optional String
+/// "libraryUri": optional String
+/// "isNotImported": optional bool
/// }
///
/// Clients may not extend, implement or mix-in this class.
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 0a14c4f..24ad02a 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.10';
+const String PROTOCOL_VERSION = '1.33.0';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analyzer/lib/src/dart/constant/compute.dart b/pkg/analyzer/lib/src/dart/constant/compute.dart
index 51188fe..6a08985 100644
--- a/pkg/analyzer/lib/src/dart/constant/compute.dart
+++ b/pkg/analyzer/lib/src/dart/constant/compute.dart
@@ -15,10 +15,7 @@
var walker = _ConstantWalker(declaredVariables, featureSet);
for (var constant in constants) {
- var node = walker._getNode(constant);
- if (!node.isEvaluated) {
- walker.walk(node);
- }
+ walker.walk(walker._getNode(constant));
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index e782c15..d420c963 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -347,6 +347,52 @@
return null;
}
+ /// Computes the set of free type parameters appearing in [rootType].
+ ///
+ /// If a non-null [candidates] set is given, then only type parameters
+ /// appearing in it are considered; otherwise all type parameters are
+ /// considered.
+ List<TypeParameterElement>? getFreeParameters(DartType rootType,
+ {Set<TypeParameterElement>? candidates}) {
+ List<TypeParameterElement>? parameters;
+ Set<DartType> visitedTypes = HashSet<DartType>();
+ Set<TypeParameterElement> boundTypeParameters =
+ HashSet<TypeParameterElement>();
+
+ void appendParameters(DartType? type) {
+ if (type == null) {
+ return;
+ }
+ if (visitedTypes.contains(type)) {
+ return;
+ }
+ visitedTypes.add(type);
+ if (type is TypeParameterType) {
+ var element = type.element;
+ if ((candidates == null || candidates.contains(element)) &&
+ !boundTypeParameters.contains(element)) {
+ parameters ??= <TypeParameterElement>[];
+ parameters!.add(element);
+ }
+ } else {
+ if (type is FunctionType) {
+ assert(!type.typeFormals.any((t) => boundTypeParameters.contains(t)));
+ boundTypeParameters.addAll(type.typeFormals);
+ appendParameters(type.returnType);
+ type.parameters.map((p) => p.type).forEach(appendParameters);
+ // TODO(scheglov) https://github.com/dart-lang/sdk/issues/44218
+ type.alias?.typeArguments.forEach(appendParameters);
+ boundTypeParameters.removeAll(type.typeFormals);
+ } else if (type is InterfaceType) {
+ type.typeArguments.forEach(appendParameters);
+ }
+ }
+ }
+
+ appendParameters(rootType);
+ return parameters;
+ }
+
/// Computes the greatest lower bound of [T1] and [T2].
DartType getGreatestLowerBound(DartType T1, DartType T2) {
return _greatestLowerBoundHelper.getGreatestLowerBound(T1, T2);
@@ -541,46 +587,12 @@
}
}
- List<TypeParameterElement>? getFreeParameters(DartType rootType) {
- List<TypeParameterElement>? parameters;
- Set<DartType> visitedTypes = HashSet<DartType>();
-
- void appendParameters(DartType? type) {
- if (type == null) {
- return;
- }
- if (visitedTypes.contains(type)) {
- return;
- }
- visitedTypes.add(type);
- if (type is TypeParameterType) {
- var element = type.element;
- if (all.contains(element)) {
- parameters ??= <TypeParameterElement>[];
- parameters!.add(element);
- }
- } else {
- if (type is FunctionType) {
- appendParameters(type.returnType);
- type.parameters.map((p) => p.type).forEach(appendParameters);
- // TODO(scheglov) https://github.com/dart-lang/sdk/issues/44218
- type.alias?.typeArguments.forEach(appendParameters);
- } else if (type is InterfaceType) {
- type.typeArguments.forEach(appendParameters);
- }
- }
- }
-
- appendParameters(rootType);
- return parameters;
- }
-
bool hasProgress = true;
while (hasProgress) {
hasProgress = false;
for (TypeParameterElement parameter in partials.keys) {
DartType value = partials[parameter]!;
- var freeParameters = getFreeParameters(value);
+ var freeParameters = getFreeParameters(value, candidates: all);
if (freeParameters == null) {
defaults[parameter] = value;
partials.remove(parameter);
diff --git a/pkg/analyzer/lib/src/summary2/simply_bounded.dart b/pkg/analyzer/lib/src/summary2/simply_bounded.dart
index 4a153a5..660df6f 100644
--- a/pkg/analyzer/lib/src/summary2/simply_bounded.dart
+++ b/pkg/analyzer/lib/src/summary2/simply_bounded.dart
@@ -37,9 +37,7 @@
}
for (var node in nodes) {
- if (!node.isEvaluated) {
- walker.walk(node);
- }
+ walker.walk(node);
var node2 = node._node;
if (node2 is ClassOrMixinDeclaration) {
var element = node2.declaredElement as ClassElementImpl;
diff --git a/pkg/analyzer/lib/src/summary2/top_level_inference.dart b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
index 45b2edb..9708fbb 100644
--- a/pkg/analyzer/lib/src/summary2/top_level_inference.dart
+++ b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
@@ -331,9 +331,7 @@
void walkNodes() {
for (var node in _nodes.values) {
- if (!node.isEvaluated) {
- walk(node);
- }
+ walk(node);
}
}
}
@@ -424,9 +422,7 @@
@override
void perform() {
- if (!_node.isEvaluated) {
- _node._walker.walk(_node);
- }
+ _node._walker.walk(_node);
}
}
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index 1df2f8e..442a175 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1149,6 +1149,37 @@
The type of the options parameter being suggested. This field is
omitted if the parameterName field is omitted.
</p>
+ </dd><dt class="field"><b>libraryUri: String<span style="color:#999999"> (optional)</span></b></dt><dd>
+
+ <p>
+ This field is omitted if <tt>getSuggestions</tt> was used rather
+ than <tt>getSuggestions2</tt>.
+ </p>
+ <p>
+ This field is omitted if this suggestion corresponds to a locally
+ declared element.
+ </p>
+ <p>
+ If this suggestion corresponds to an already imported element,
+ then this field is the URI of a library that provides this element,
+ not the URI of the library where the element is declared.
+ </p>
+ <p>
+ If this suggestion corresponds to an element from a not yet
+ imported library, this field is the URI of a library that could be
+ imported to make this suggestion accessible in the file where
+ completion was requested, such as <tt>package:foo/bar.dart</tt> or
+ <tt>file:///home/me/workspace/foo/test/bar_test.dart</tt>.
+ </p>
+ </dd><dt class="field"><b>isNotImported: bool<span style="color:#999999"> (optional)</span></b></dt><dd>
+
+ <p>
+ True if the suggestion is for an element from a not yet imported
+ library. This field is omitted if the element is declared locally,
+ or is from library is already imported, so that the suggestion can
+ be inserted as is, or if <tt>getSuggestions</tt> was used rather
+ than <tt>getSuggestions2</tt>.
+ </p>
</dd></dl></dd><dt class="typeDefinition"><a name="type_CompletionSuggestionKind">CompletionSuggestionKind: String</a></dt><dd>
<p>
An enumeration of the kinds of elements that can be included in a
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 32fe3b2..85dd6d6 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -504,6 +504,8 @@
/// "hasNamedParameters": optional bool
/// "parameterName": optional String
/// "parameterType": optional String
+/// "libraryUri": optional String
+/// "isNotImported": optional bool
/// }
///
/// Clients may not extend, implement or mix-in this class.
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index cfa4524..9639648 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -138,6 +138,8 @@
/// "hasNamedParameters": optional bool
/// "parameterName": optional String
/// "parameterType": optional String
+/// "libraryUri": optional String
+/// "isNotImported": optional bool
/// }
final Matcher isCompletionSuggestion =
LazyMatcher(() => MatchesJsonObject('CompletionSuggestion', {
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index a12e921..ca100b9 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -380,7 +380,7 @@
omitted if the parameterName field is omitted.
</p>
</field>
- <field name="libraryUri" experimental="true" optional="true">
+ <field name="libraryUri" optional="true">
<ref>String</ref>
<p>
This field is omitted if <tt>getSuggestions</tt> was used rather
@@ -403,7 +403,7 @@
<tt>file:///home/me/workspace/foo/test/bar_test.dart</tt>.
</p>
</field>
- <field name="isNotImported" optional="true" experimental="true">
+ <field name="isNotImported" optional="true">
<ref>bool</ref>
<p>
True if the suggestion is for an element from a not yet imported
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index ccae7f7..fcdf267 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -95,6 +95,8 @@
ResolutionEnqueuer resolutionEnqueuerForTesting;
CodegenEnqueuer codegenEnqueuerForTesting;
+ ir.Component untrimmedComponentForDumpInfo;
+
DiagnosticReporter get reporter => _reporter;
Map<Entity, WorldImpact> get impactCache => _impactCache;
@@ -387,6 +389,9 @@
if (retainDataForTesting) {
componentForTesting = output.component;
}
+ if (options.features.newDumpInfo.isEnabled && options.dumpInfo) {
+ untrimmedComponentForDumpInfo = output.component;
+ }
if (options.cfeOnly) {
ir.Component component = output.component;
@@ -439,7 +444,7 @@
mainFunction, closedWorld, globalLocalsMap, inferredDataBuilder);
}
- void runCodegenEnqueuer(CodegenResults codegenResults) {
+ int runCodegenEnqueuer(CodegenResults codegenResults) {
GlobalTypeInferenceResults globalInferenceResults =
codegenResults.globalTypeInferenceResults;
JClosedWorld closedWorld = globalInferenceResults.closedWorld;
@@ -469,14 +474,10 @@
int programSize = backendStrategy.assembleProgram(closedWorld,
globalInferenceResults.inferredData, codegenInputs, codegenWorld);
- if (options.dumpInfo) {
- dumpInfoTask.reportSize(programSize);
- dumpInfoTask.dumpInfo(closedWorld, globalInferenceResults);
- }
-
backendStrategy.onCodegenEnd(codegenInputs);
checkQueue(codegenEnqueuer);
+ return programSize;
}
GlobalTypeInferenceResults globalTypeInferenceResultsTestMode(
@@ -640,10 +641,30 @@
if (shouldStopAfterCodegen) return;
// Link.
- runCodegenEnqueuer(codegenResults);
+ int programSize = runCodegenEnqueuer(codegenResults);
+
+ // Dump Info.
+ if (options.dumpInfo) {
+ runDumpInfo(codegenResults, programSize);
+ }
});
}
+ void runDumpInfo(CodegenResults codegenResults, int programSize) {
+ GlobalTypeInferenceResults globalTypeInferenceResults =
+ codegenResults.globalTypeInferenceResults;
+ JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+
+ dumpInfoTask.reportSize(programSize);
+ if (options.features.newDumpInfo.isEnabled) {
+ assert(untrimmedComponentForDumpInfo != null);
+ dumpInfoTask.dumpInfoNew(untrimmedComponentForDumpInfo, closedWorld,
+ globalTypeInferenceResults);
+ } else {
+ dumpInfoTask.dumpInfo(closedWorld, globalTypeInferenceResults);
+ }
+ }
+
/// Perform the steps needed to fully end the resolution phase.
JClosedWorld closeResolution(FunctionEntity mainFunction,
ResolutionWorldBuilder resolutionWorldBuilder) {
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index c472ba5..77d8144 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -10,6 +10,7 @@
import 'package:dart2js_info/info.dart';
import 'package:dart2js_info/json_info_codec.dart';
import 'package:dart2js_info/binary_serialization.dart' as dump_info;
+import 'package:kernel/ast.dart' as ir;
import '../compiler.dart';
import 'common.dart';
@@ -407,6 +408,384 @@
}
}
+class KernelInfoCollector {
+ final ir.Component component;
+ final Compiler compiler;
+ final JClosedWorld closedWorld;
+ final GlobalTypeInferenceResults _globalInferenceResults;
+ final DumpInfoTask dumpInfoTask;
+
+ JElementEnvironment get environment => closedWorld.elementEnvironment;
+
+ final AllInfo result = AllInfo();
+ final Map<Entity, Info> _entityToInfo = <Entity, Info>{};
+ final Map<ConstantValue, Info> _constantToInfo = <ConstantValue, Info>{};
+ final Map<OutputUnit, OutputUnitInfo> _outputToInfo = {};
+
+ KernelInfoCollector(this.component, this.compiler, this.dumpInfoTask,
+ this.closedWorld, this._globalInferenceResults);
+
+ void run() {
+ dumpInfoTask._constantToNode.forEach((constant, node) {
+ // TODO(sigmund): add dependencies on other constants
+ var span = dumpInfoTask._nodeData[node];
+ var info = ConstantInfo(
+ size: span.end - span.start,
+ code: [span],
+ outputUnit: _unitInfoForConstant(constant));
+ _constantToInfo[constant] = info;
+ result.constants.add(info);
+ });
+ environment.libraries.forEach(visitLibrary);
+ }
+
+ /// Whether to emit information about [entity].
+ ///
+ /// By default we emit information for any entity that contributes to the
+ /// output size. Either because it is a function being emitted or inlined,
+ /// or because it is an entity that holds dependencies to other entities.
+ bool shouldKeep(Entity entity) {
+ return dumpInfoTask.impacts.containsKey(entity) ||
+ dumpInfoTask.inlineCount.containsKey(entity);
+ }
+
+ LibraryInfo visitLibrary(LibraryEntity lib) {
+ String libname = environment.getLibraryName(lib);
+ if (libname.isEmpty) {
+ libname = '<unnamed>';
+ }
+ int size = dumpInfoTask.sizeOf(lib);
+ LibraryInfo info = LibraryInfo(libname, lib.canonicalUri, null, size);
+ _entityToInfo[lib] = info;
+
+ environment.forEachLibraryMember(lib, (MemberEntity member) {
+ if (member.isFunction || member.isGetter || member.isSetter) {
+ FunctionInfo functionInfo = visitFunction(member);
+ if (functionInfo != null) {
+ info.topLevelFunctions.add(functionInfo);
+ functionInfo.parent = info;
+ }
+ } else if (member.isField) {
+ FieldInfo fieldInfo = visitField(member);
+ if (fieldInfo != null) {
+ info.topLevelVariables.add(fieldInfo);
+ fieldInfo.parent = info;
+ }
+ }
+ });
+
+ environment.forEachClass(lib, (ClassEntity clazz) {
+ ClassTypeInfo classTypeInfo = visitClassType(clazz);
+ if (classTypeInfo != null) {
+ info.classTypes.add(classTypeInfo);
+ classTypeInfo.parent = info;
+ }
+
+ ClassInfo classInfo = visitClass(clazz);
+ if (classInfo != null) {
+ info.classes.add(classInfo);
+ classInfo.parent = info;
+ }
+ });
+
+ if (info.isEmpty && !shouldKeep(lib)) return null;
+ result.libraries.add(info);
+ return info;
+ }
+
+ GlobalTypeInferenceMemberResult _resultOfMember(MemberEntity e) =>
+ _globalInferenceResults.resultOfMember(e);
+
+ AbstractValue _resultOfParameter(Local e) =>
+ _globalInferenceResults.resultOfParameter(e);
+
+ FieldInfo visitField(FieldEntity field, {ClassEntity containingClass}) {
+ AbstractValue inferredType = _resultOfMember(field).type;
+ // If a field has an empty inferred type it is never used.
+ if (inferredType == null ||
+ closedWorld.abstractValueDomain
+ .isEmpty(inferredType)
+ .isDefinitelyTrue) {
+ return null;
+ }
+
+ int size = dumpInfoTask.sizeOf(field);
+ List<CodeSpan> code = dumpInfoTask.codeOf(field);
+
+ // TODO(het): Why doesn't `size` account for the code size already?
+ if (code != null) size += code.length;
+
+ FieldInfo info = FieldInfo(
+ name: field.name,
+ type: '${environment.getFieldType(field)}',
+ inferredType: '$inferredType',
+ code: code,
+ outputUnit: _unitInfoForMember(field),
+ isConst: field.isConst);
+ _entityToInfo[field] = info;
+ FieldAnalysisData fieldData = closedWorld.fieldAnalysis.getFieldData(field);
+ if (fieldData.initialValue != null) {
+ info.initializer = _constantToInfo[fieldData.initialValue];
+ }
+
+ if (compiler.options.experimentCallInstrumentation) {
+ // We use field.hashCode because it is globally unique and it is
+ // available while we are doing codegen.
+ info.coverageId = '${field.hashCode}';
+ }
+
+ int closureSize = _addClosureInfo(info, field);
+ info.size = size + closureSize;
+
+ result.fields.add(info);
+ return info;
+ }
+
+ ClassTypeInfo visitClassType(ClassEntity clazz) {
+ // Omit class type if it is not needed.
+ ClassTypeInfo classTypeInfo = ClassTypeInfo(
+ name: clazz.name, outputUnit: _unitInfoForClassType(clazz));
+
+ // TODO(joshualitt): Get accurate size information for class types.
+ classTypeInfo.size = 0;
+
+ bool isNeeded =
+ compiler.backendStrategy.emitterTask.neededClassTypes.contains(clazz);
+ if (!isNeeded) {
+ return null;
+ }
+
+ result.classTypes.add(classTypeInfo);
+ return classTypeInfo;
+ }
+
+ ClassInfo visitClass(ClassEntity clazz) {
+ // Omit class if it is not needed.
+ ClassInfo classInfo = ClassInfo(
+ name: clazz.name,
+ isAbstract: clazz.isAbstract,
+ outputUnit: _unitInfoForClass(clazz));
+ _entityToInfo[clazz] = classInfo;
+
+ int size = dumpInfoTask.sizeOf(clazz);
+ environment.forEachLocalClassMember(clazz, (member) {
+ if (member.isFunction || member.isGetter || member.isSetter) {
+ FunctionInfo functionInfo = visitFunction(member);
+ if (functionInfo != null) {
+ classInfo.functions.add(functionInfo);
+ functionInfo.parent = classInfo;
+ for (var closureInfo in functionInfo.closures) {
+ size += closureInfo.size;
+ }
+ }
+ } else if (member.isField) {
+ FieldInfo fieldInfo = visitField(member, containingClass: clazz);
+ if (fieldInfo != null) {
+ classInfo.fields.add(fieldInfo);
+ fieldInfo.parent = classInfo;
+ for (var closureInfo in fieldInfo.closures) {
+ size += closureInfo.size;
+ }
+ }
+ } else {
+ throw StateError('Class member not a function or field');
+ }
+ });
+ environment.forEachConstructor(clazz, (constructor) {
+ FunctionInfo functionInfo = visitFunction(constructor);
+ if (functionInfo != null) {
+ classInfo.functions.add(functionInfo);
+ functionInfo.parent = classInfo;
+ for (var closureInfo in functionInfo.closures) {
+ size += closureInfo.size;
+ }
+ }
+ });
+
+ classInfo.size = size;
+
+ if (!compiler.backendStrategy.emitterTask.neededClasses.contains(clazz) &&
+ classInfo.fields.isEmpty &&
+ classInfo.functions.isEmpty) {
+ return null;
+ }
+
+ result.classes.add(classInfo);
+ return classInfo;
+ }
+
+ ClosureInfo visitClosureClass(ClassEntity element) {
+ ClosureInfo closureInfo = ClosureInfo(
+ name: element.name,
+ outputUnit: _unitInfoForClass(element),
+ size: dumpInfoTask.sizeOf(element));
+ _entityToInfo[element] = closureInfo;
+
+ FunctionEntity callMethod = closedWorld.elementEnvironment
+ .lookupClassMember(element, Identifiers.call);
+
+ FunctionInfo functionInfo = visitFunction(callMethod);
+ if (functionInfo == null) return null;
+ closureInfo.function = functionInfo;
+ functionInfo.parent = closureInfo;
+
+ result.closures.add(closureInfo);
+ return closureInfo;
+ }
+
+ FunctionInfo visitFunction(FunctionEntity function) {
+ int size = dumpInfoTask.sizeOf(function);
+ // TODO(sigmund): consider adding a small info to represent unreachable
+ // code here.
+ if (size == 0 && !shouldKeep(function)) return null;
+
+ // TODO(het): use 'toString' instead of 'text'? It will add '=' for setters
+ String name = function.memberName.text;
+ int kind;
+ if (function.isStatic || function.isTopLevel) {
+ kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND;
+ } else if (function.enclosingClass != null) {
+ kind = FunctionInfo.METHOD_FUNCTION_KIND;
+ }
+
+ if (function.isConstructor) {
+ name = name == ""
+ ? "${function.enclosingClass.name}"
+ : "${function.enclosingClass.name}.${function.name}";
+ kind = FunctionInfo.CONSTRUCTOR_FUNCTION_KIND;
+ }
+
+ assert(kind != null);
+
+ FunctionModifiers modifiers = FunctionModifiers(
+ isStatic: function.isStatic,
+ isConst: function.isConst,
+ isFactory: function.isConstructor
+ ? (function as ConstructorEntity).isFactoryConstructor
+ : false,
+ isExternal: function.isExternal,
+ );
+ List<CodeSpan> code = dumpInfoTask.codeOf(function);
+
+ List<ParameterInfo> parameters = <ParameterInfo>[];
+ List<String> inferredParameterTypes = <String>[];
+
+ closedWorld.elementEnvironment.forEachParameterAsLocal(
+ _globalInferenceResults.globalLocalsMap, function, (parameter) {
+ inferredParameterTypes.add('${_resultOfParameter(parameter)}');
+ });
+ int parameterIndex = 0;
+ closedWorld.elementEnvironment.forEachParameter(function, (type, name, _) {
+ // Synthesized parameters have no name. This can happen on parameters of
+ // setters derived from lowering late fields.
+ parameters.add(ParameterInfo(name ?? '#t${parameterIndex}',
+ inferredParameterTypes[parameterIndex++], '$type'));
+ });
+
+ var functionType = environment.getFunctionType(function);
+ String returnType = '${functionType.returnType}';
+
+ String inferredReturnType = '${_resultOfMember(function).returnType}';
+ String sideEffects =
+ '${_globalInferenceResults.inferredData.getSideEffectsOfElement(function)}';
+
+ int inlinedCount = dumpInfoTask.inlineCount[function];
+ if (inlinedCount == null) inlinedCount = 0;
+
+ FunctionInfo info = FunctionInfo(
+ name: name,
+ functionKind: kind,
+ modifiers: modifiers,
+ returnType: returnType,
+ inferredReturnType: inferredReturnType,
+ parameters: parameters,
+ sideEffects: sideEffects,
+ inlinedCount: inlinedCount,
+ code: code,
+ type: functionType.toString(),
+ outputUnit: _unitInfoForMember(function));
+ _entityToInfo[function] = info;
+
+ int closureSize = _addClosureInfo(info, function);
+ size += closureSize;
+
+ if (compiler.options.experimentCallInstrumentation) {
+ // We use function.hashCode because it is globally unique and it is
+ // available while we are doing codegen.
+ info.coverageId = '${function.hashCode}';
+ }
+
+ info.size = size;
+
+ result.functions.add(info);
+ return info;
+ }
+
+ /// Adds closure information to [info], using all nested closures in [member].
+ ///
+ /// Returns the total size of the nested closures, to add to the info size.
+ int _addClosureInfo(Info info, MemberEntity member) {
+ assert(info is FunctionInfo || info is FieldInfo);
+ int size = 0;
+ List<ClosureInfo> nestedClosures = <ClosureInfo>[];
+ environment.forEachNestedClosure(member, (closure) {
+ ClosureInfo closureInfo = visitClosureClass(closure.enclosingClass);
+ if (closureInfo != null) {
+ closureInfo.parent = info;
+ nestedClosures.add(closureInfo);
+ size += closureInfo.size;
+ }
+ });
+ if (info is FunctionInfo) info.closures = nestedClosures;
+ if (info is FieldInfo) info.closures = nestedClosures;
+
+ return size;
+ }
+
+ OutputUnitInfo _infoFromOutputUnit(OutputUnit outputUnit) {
+ return _outputToInfo.putIfAbsent(outputUnit, () {
+ // Dump-info currently only works with the full emitter. If another
+ // emitter is used it will fail here.
+ JsBackendStrategy backendStrategy = compiler.backendStrategy;
+ assert(outputUnit.name != null || outputUnit.isMainOutput);
+ var filename = outputUnit.isMainOutput
+ ? compiler.options.outputUri.pathSegments.last
+ : deferredPartFileName(compiler.options, outputUnit.name);
+ OutputUnitInfo info = OutputUnitInfo(filename, outputUnit.name,
+ backendStrategy.emitterTask.emitter.generatedSize(outputUnit));
+ info.imports
+ .addAll(closedWorld.outputUnitData.getImportNames(outputUnit));
+ result.outputUnits.add(info);
+ return info;
+ });
+ }
+
+ OutputUnitInfo _unitInfoForMember(MemberEntity entity) {
+ return _infoFromOutputUnit(
+ closedWorld.outputUnitData.outputUnitForMember(entity));
+ }
+
+ OutputUnitInfo _unitInfoForClass(ClassEntity entity) {
+ return _infoFromOutputUnit(
+ closedWorld.outputUnitData.outputUnitForClass(entity, allowNull: true));
+ }
+
+ OutputUnitInfo _unitInfoForClassType(ClassEntity entity) {
+ return _infoFromOutputUnit(closedWorld.outputUnitData
+ .outputUnitForClassType(entity, allowNull: true));
+ }
+
+ OutputUnitInfo _unitInfoForConstant(ConstantValue constant) {
+ OutputUnit outputUnit =
+ closedWorld.outputUnitData.outputUnitForConstant(constant);
+ if (outputUnit == null) {
+ assert(constant is InterceptorConstantValue);
+ return null;
+ }
+ return _infoFromOutputUnit(outputUnit);
+ }
+}
+
class Selection {
final Entity selectedEntity;
final Object receiverConstraint;
@@ -434,8 +813,6 @@
@override
String get name => "Dump Info";
- ElementInfoCollector infoCollector;
-
/// The size of the generated output.
int _programSize;
@@ -578,11 +955,27 @@
void dumpInfo(JClosedWorld closedWorld,
GlobalTypeInferenceResults globalInferenceResults) {
measure(() {
- infoCollector = ElementInfoCollector(
+ ElementInfoCollector elementInfoCollector = ElementInfoCollector(
compiler, this, closedWorld, globalInferenceResults)
..run();
- var allInfo = buildDumpInfoData(closedWorld);
+ var allInfo = buildDumpInfoData(closedWorld, elementInfoCollector);
+ if (useBinaryFormat) {
+ dumpInfoBinary(allInfo);
+ } else {
+ dumpInfoJson(allInfo);
+ }
+ });
+ }
+
+ void dumpInfoNew(ir.Component component, JClosedWorld closedWorld,
+ GlobalTypeInferenceResults globalInferenceResults) {
+ measure(() {
+ KernelInfoCollector kernelInfoCollector = KernelInfoCollector(
+ component, compiler, this, closedWorld, globalInferenceResults)
+ ..run();
+
+ var allInfo = buildDumpInfoDataNew(closedWorld, kernelInfoCollector);
if (useBinaryFormat) {
dumpInfoBinary(allInfo);
} else {
@@ -622,7 +1015,84 @@
});
}
- AllInfo buildDumpInfoData(JClosedWorld closedWorld) {
+ AllInfo buildDumpInfoData(
+ JClosedWorld closedWorld, ElementInfoCollector infoCollector) {
+ Stopwatch stopwatch = Stopwatch();
+ stopwatch.start();
+
+ AllInfo result = infoCollector.result;
+
+ // Recursively build links to function uses
+ Iterable<Entity> functionEntities =
+ infoCollector._entityToInfo.keys.where((k) => k is FunctionEntity);
+ for (FunctionEntity entity in functionEntities) {
+ FunctionInfo info = infoCollector._entityToInfo[entity];
+ Iterable<Selection> uses = getRetaining(entity, closedWorld);
+ // Don't bother recording an empty list of dependencies.
+ for (Selection selection in uses) {
+ // Don't register dart2js builtin functions that are not recorded.
+ Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
+ if (useInfo == null) continue;
+ info.uses.add(
+ DependencyInfo(useInfo, selection.receiverConstraint?.toString()));
+ }
+ }
+
+ // Recursively build links to field uses
+ Iterable<Entity> fieldEntity =
+ infoCollector._entityToInfo.keys.where((k) => k is FieldEntity);
+ for (FieldEntity entity in fieldEntity) {
+ FieldInfo info = infoCollector._entityToInfo[entity];
+ Iterable<Selection> uses = getRetaining(entity, closedWorld);
+ // Don't bother recording an empty list of dependencies.
+ for (Selection selection in uses) {
+ Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
+ if (useInfo == null) continue;
+ info.uses.add(
+ DependencyInfo(useInfo, selection.receiverConstraint?.toString()));
+ }
+ }
+
+ // Track dependencies that come from inlining.
+ for (Entity entity in inlineMap.keys) {
+ CodeInfo outerInfo = infoCollector._entityToInfo[entity];
+ if (outerInfo == null) continue;
+ for (Entity inlined in inlineMap[entity]) {
+ Info inlinedInfo = infoCollector._entityToInfo[inlined];
+ if (inlinedInfo == null) continue;
+ outerInfo.uses.add(DependencyInfo(inlinedInfo, 'inlined'));
+ }
+ }
+
+ var fragmentsToLoad =
+ compiler.backendStrategy.emitterTask.emitter.finalizedFragmentsToLoad;
+ var fragmentMerger =
+ compiler.backendStrategy.emitterTask.emitter.fragmentMerger;
+ result.deferredFiles = fragmentMerger.computeDeferredMap(fragmentsToLoad);
+ stopwatch.stop();
+
+ result.program = ProgramInfo(
+ entrypoint: infoCollector
+ ._entityToInfo[closedWorld.elementEnvironment.mainFunction],
+ size: _programSize,
+ dart2jsVersion:
+ compiler.options.hasBuildId ? compiler.options.buildId : null,
+ compilationMoment: DateTime.now(),
+ compilationDuration: compiler.measurer.elapsedWallClock,
+ toJsonDuration: Duration(milliseconds: stopwatch.elapsedMilliseconds),
+ dumpInfoDuration: Duration(milliseconds: this.timing),
+ noSuchMethodEnabled: closedWorld.backendUsage.isNoSuchMethodUsed,
+ isRuntimeTypeUsed: closedWorld.backendUsage.isRuntimeTypeUsed,
+ isIsolateInUse: false,
+ isFunctionApplyUsed: closedWorld.backendUsage.isFunctionApplyUsed,
+ isMirrorsUsed: closedWorld.backendUsage.isMirrorsUsed,
+ minified: compiler.options.enableMinification);
+
+ return result;
+ }
+
+ AllInfo buildDumpInfoDataNew(
+ JClosedWorld closedWorld, KernelInfoCollector infoCollector) {
Stopwatch stopwatch = Stopwatch();
stopwatch.start();
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index 0f1034d..311d7c8 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -32,7 +32,10 @@
final codegenInputs = compiler.initializeCodegen(globalTypeInferenceResults);
final codegenResults = OnDemandCodegenResults(globalTypeInferenceResults,
codegenInputs, compiler.backendStrategy.functionCompiler);
- compiler.runCodegenEnqueuer(codegenResults);
+ final programSize = compiler.runCodegenEnqueuer(codegenResults);
+ if (compiler.options.dumpInfo) {
+ compiler.runDumpInfo(codegenResults, programSize);
+ }
}
void finishCompileAndCompare(
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index ab9d049..d095e3b 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -217,7 +217,7 @@
: restart = obj['restart'],
name = obj['name'] as String?,
cwd = obj['cwd'] as String?,
- env = obj['env'] as Map<String, String>?,
+ env = (obj['env'] as Map<String, Object?>?)?.cast<String, String>(),
additionalProjectPaths =
(obj['additionalProjectPaths'] as List?)?.cast<String>(),
debugSdkLibraries = obj['debugSdkLibraries'] as bool?,
diff --git a/pkg/dds/test/dap/arguments_test.dart b/pkg/dds/test/dap/arguments_test.dart
new file mode 100644
index 0000000..d5b0b72
--- /dev/null
+++ b/pkg/dds/test/dap/arguments_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:dds/dap.dart';
+import 'package:test/test.dart';
+
+main() {
+ group('DartLaunchRequestArguments', () {
+ test('handles only required arguments', () async {
+ final json = '{"program":"a"}';
+ final decoded = DartLaunchRequestArguments.fromJson(jsonDecode(json));
+ expect(decoded.program, 'a');
+ final encoded = jsonEncode(decoded.toJson());
+ expect(encoded, json);
+ });
+
+ test('handles env variables map', () async {
+ final json = '{"env":{"a":"b"},"program":"a"}';
+ final decoded = DartLaunchRequestArguments.fromJson(jsonDecode(json));
+ expect(decoded.env!['a'], 'b');
+ final encoded = jsonEncode(decoded.toJson());
+ expect(encoded, json);
+ });
+
+ test('handles additional project paths list', () async {
+ final json = '{"additionalProjectPaths":["a","b"],"program":"a"}';
+ final decoded = DartLaunchRequestArguments.fromJson(jsonDecode(json));
+ expect(decoded.additionalProjectPaths, ['a', 'b']);
+ final encoded = jsonEncode(decoded.toJson());
+ expect(encoded, json);
+ });
+ });
+}
diff --git a/pkg/dev_compiler/lib/src/js_ast/builder.dart b/pkg/dev_compiler/lib/src/js_ast/builder.dart
index a7ba76a..6ba6a81 100644
--- a/pkg/dev_compiler/lib/src/js_ast/builder.dart
+++ b/pkg/dev_compiler/lib/src/js_ast/builder.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
// ignore_for_file: always_declare_return_types
// ignore_for_file: library_prefixes
// ignore_for_file: non_constant_identifier_names
@@ -144,7 +142,7 @@
/// blocks to be appended.
///
/// var b1 = js.statement('{ 1; 2; }');
-/// var sEmpty = new Emptystatement();
+/// var sEmpty = new EmptyStatement();
/// js.statement('{ #; #; #; #; }', [sEmpty, b1, b1, sEmpty])
/// -->
/// { 1; 2; 1; 2; }
@@ -212,7 +210,7 @@
/// [arguments] can be a single [Node] (e.g. an [Expression] or [Statement])
/// or a list of [Node]s, which will be interpolated into the source at the
/// '#' signs.
- Expression call(String source, [arguments]) {
+ Expression call(String source, [Object? arguments]) {
Template template = _findExpressionTemplate(source);
if (arguments == null) return template.instantiate([]) as Expression;
// We allow a single argument to be given directly.
@@ -221,7 +219,7 @@
}
/// Parses a JavaScript Statement, otherwise just like [call].
- Statement statement(String source, [arguments]) {
+ Statement statement(String source, [Object? arguments]) {
Template template = _findStatementTemplate(source);
if (arguments == null) return template.instantiate([]) as Statement;
// We allow a single argument to be given directly.
@@ -249,7 +247,7 @@
}
Template _findExpressionTemplate(String source) {
- Template template = templateManager.lookupExpressionTemplate(source);
+ Template? template = templateManager.lookupExpressionTemplate(source);
if (template == null) {
MiniJsParser parser = MiniJsParser(source);
Expression expression = parser.expression();
@@ -259,7 +257,7 @@
}
Template _findStatementTemplate(String source) {
- Template template = templateManager.lookupStatementTemplate(source);
+ Template? template = templateManager.lookupStatementTemplate(source);
if (template == null) {
MiniJsParser parser = MiniJsParser(source);
Statement statement = parser.statement();
@@ -285,11 +283,11 @@
/// Create an Expression template which has [ast] as the result. This is used
/// to wrap a generated AST in a zero-argument Template so it can be passed to
/// context that expects a template.
- Template expressionTemplateYielding(Node ast) {
+ Template expressionTemplateYielding(Expression ast) {
return Template.withExpressionResult(ast);
}
- Template statementTemplateYielding(Node ast) {
+ Template statementTemplateYielding(Statement ast) {
return Template.withStatementResult(ast);
}
@@ -330,7 +328,7 @@
var sb = new StringBuffer();
for (int rune in value.runes) {
- String escape = _irregularEscape(rune, quote);
+ final escape = _irregularEscape(rune, quote);
if (escape != null) {
sb.write(escape);
continue;
@@ -359,7 +357,7 @@
static bool _isUnpairedSurrogate(int code) => (code & 0xFFFFF800) == 0xD800;
- static String _irregularEscape(int code, String quote) {
+ static String? _irregularEscape(int code, String quote) {
switch (code) {
case charCodes.$SQ:
return quote == "'" ? r"\'" : "'";
@@ -789,8 +787,8 @@
if (lastToken == ARROW_TOKEN) {
lastCategory = ARROW;
} else {
- int binaryPrecendence = BINARY_PRECEDENCE[lastToken];
- if (binaryPrecendence == null &&
+ int? binaryPrecedence = BINARY_PRECEDENCE[lastToken];
+ if (binaryPrecedence == null &&
!UNARY_OPERATORS.contains(lastToken)) {
error("Unknown operator");
}
@@ -841,12 +839,12 @@
return false;
}
- void error(String message) {
+ Never error(String message) {
throw MiniJsParserError(this, message);
}
/// Returns either the name for the hole, or its integer position.
- parseHash() {
+ Object parseHash() {
String holeName = lastToken;
if (acceptCategory(ALPHA)) {
// Named hole. Example: 'function #funName() { ... }'
@@ -905,7 +903,7 @@
expectCategory(COMMA);
}
return ArrayInitializer(values);
- } else if (last != null && last.startsWith("/")) {
+ } else if (last.startsWith("/")) {
String regexp = getDelimited(lastPosition);
getToken();
String flags = lastToken;
@@ -916,7 +914,6 @@
return parseInterpolatedExpression();
} else {
error("Expected primary expression");
- return null;
}
}
@@ -1201,27 +1198,29 @@
Expression parseBinary(int maxPrecedence) {
Expression lhs = parseUnaryLow();
- int minPrecedence;
- String lastSymbol;
- Expression rhs; // This is null first time around.
+ Expression? rhs; // This is null first time around.
+ late int minPrecedence;
+ late String lastSymbol;
+
while (true) {
- String symbol = lastToken;
- if (lastCategory != SYMBOL ||
- !BINARY_PRECEDENCE.containsKey(symbol) ||
- BINARY_PRECEDENCE[symbol] > maxPrecedence) {
- break;
- }
+ final symbol = lastToken;
+ if (lastCategory != SYMBOL) break;
+ final symbolPrecedence = BINARY_PRECEDENCE[symbol];
+ if (symbolPrecedence == null) break;
+ if (symbolPrecedence > maxPrecedence) break;
+
expectCategory(SYMBOL);
- if (rhs == null || BINARY_PRECEDENCE[symbol] >= minPrecedence) {
+ if (rhs == null || symbolPrecedence >= minPrecedence) {
if (rhs != null) lhs = Binary(lastSymbol, lhs, rhs);
- minPrecedence = BINARY_PRECEDENCE[symbol];
+ minPrecedence = symbolPrecedence;
rhs = parseUnaryLow();
lastSymbol = symbol;
} else {
- Expression higher = parseBinary(BINARY_PRECEDENCE[symbol]);
+ Expression higher = parseBinary(symbolPrecedence);
rhs = Binary(symbol, rhs, higher);
}
}
+
if (rhs == null) return lhs;
return Binary(lastSymbol, lhs, rhs);
}
@@ -1265,7 +1264,7 @@
/// Parse a variable declaration list, with `var` or `let` [keyword].
VariableDeclarationList parseVariableDeclarationList(String keyword,
- [String firstIdentifier]) {
+ [String? firstIdentifier]) {
var initialization = <VariableInitialization>[];
do {
@@ -1294,7 +1293,6 @@
return parseBindingPattern();
default:
error('Unexpected token $lastToken: ${categoryToString(lastCategory)}');
- return null;
}
}
@@ -1316,9 +1314,9 @@
ArrayBindingPattern parseArrayBindingPattern() {
var variables = <DestructuredVariable>[];
do {
- Identifier name;
- BindingPattern structure;
- Expression defaultValue;
+ late Identifier name;
+ BindingPattern? structure;
+ Expression? defaultValue;
var declarator = parseVariableBinding();
if (declarator is Identifier) {
@@ -1344,8 +1342,8 @@
var variables = <DestructuredVariable>[];
do {
var name = parseIdentifier();
- BindingPattern structure;
- Expression defaultValue;
+ BindingPattern? structure;
+ Expression? defaultValue;
if (acceptCategory(COLON)) {
structure = parseBindingPattern();
@@ -1370,7 +1368,7 @@
}
/// Accepts a `var` or `let` keyword. If neither is found, returns null.
- String acceptVarLetOrConst() {
+ String? acceptVarLetOrConst() {
if (acceptString('var')) return 'var';
if (acceptString('let')) return 'let';
if (acceptString('const')) return 'const';
@@ -1507,7 +1505,7 @@
return Throw(expression);
}
- Statement parseBreakOrContinue(Statement Function(String) constructor) {
+ Statement parseBreakOrContinue(Statement Function(String?) constructor) {
var identifier = lastToken;
if (!skippedNewline && acceptCategory(ALPHA)) {
expectSemicolon();
@@ -1542,13 +1540,13 @@
//
// for (let variable of Expression) Statement
//
- Statement finishFor(Expression init) {
- Expression condition;
+ Statement finishFor(Expression? init) {
+ Expression? condition;
if (!acceptCategory(SEMICOLON)) {
condition = parseExpression();
expectCategory(SEMICOLON);
}
- Expression update;
+ Expression? update;
if (!acceptCategory(RPAREN)) {
update = parseExpression();
expectCategory(RPAREN);
@@ -1606,9 +1604,9 @@
Statement parseTry() {
expectCategory(LBRACE);
Block body = parseBlock();
- Catch catchPart;
+ Catch? catchPart;
if (acceptString('catch')) catchPart = parseCatch();
- Block finallyPart;
+ Block? finallyPart;
if (acceptString('finally')) {
expectCategory(LBRACE);
finallyPart = parseBlock();
@@ -1619,7 +1617,7 @@
}
SwitchClause parseSwitchClause() {
- Expression expression;
+ Expression? expression;
if (acceptString('case')) {
expression = parseExpression();
expectCategory(COLON);
@@ -1684,7 +1682,7 @@
ClassExpression parseClass() {
Identifier name = parseIdentifier();
- Expression heritage;
+ Expression? heritage;
if (acceptString('extends')) {
heritage = parseConditional();
}
@@ -1711,7 +1709,7 @@
bool isGetter = lastToken == 'get';
bool isSetter = lastToken == 'set';
- Expression name;
+ Expression? name;
if (isGetter || isSetter) {
var token = lastToken;
getToken();
@@ -1758,7 +1756,6 @@
return parseInterpolatedExpression();
} else {
error('Expected property name');
- return null;
}
}
}
diff --git a/pkg/dev_compiler/lib/src/js_ast/template.dart b/pkg/dev_compiler/lib/src/js_ast/template.dart
index 71b7662..9ceb51f 100644
--- a/pkg/dev_compiler/lib/src/js_ast/template.dart
+++ b/pkg/dev_compiler/lib/src/js_ast/template.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
// ignore_for_file: always_declare_return_types
// ignore_for_file: avoid_returning_null_for_void
// ignore_for_file: omit_local_variable_types
@@ -22,7 +20,7 @@
TemplateManager();
- Template lookupExpressionTemplate(String source) {
+ Template? lookupExpressionTemplate(String source) {
return expressionTemplates[source];
}
@@ -33,7 +31,7 @@
return template;
}
- Template lookupStatementTemplate(String source) {
+ Template? lookupStatementTemplate(String source) {
return statementTemplates[source];
}
@@ -51,63 +49,71 @@
/// The [instantiate] method creates an AST that looks like the original with
/// the placeholders replaced by the arguments to [instantiate].
class Template {
- final String source;
+ final String? source;
final bool isExpression;
final bool forceCopy;
final Node ast;
-
- Instantiator instantiator;
-
+ final Instantiator instantiator;
int positionalArgumentCount = -1;
// Null, unless there are named holes.
List<String> holeNames;
- bool get isPositional => holeNames == null;
+ bool get isPositional => holeNames.isEmpty;
- Template(this.source, this.ast,
- {this.isExpression = true, this.forceCopy = false}) {
- _compile();
+ Template._(this.source, this.ast,
+ {required this.instantiator,
+ required this.isExpression,
+ required this.forceCopy,
+ required this.positionalArgumentCount,
+ this.holeNames = const []});
+
+ factory Template(String? source, Node ast,
+ {bool isExpression = true, bool forceCopy = false}) {
+ assert(isExpression ? ast is Expression : ast is Statement);
+
+ final generator = InstantiatorGeneratorVisitor(forceCopy);
+ final instantiator = generator.compile(ast);
+ final positionalArgumentCount = generator.analysis.count;
+ final names = generator.analysis.holeNames;
+ final holeNames = names.toList(growable: false);
+
+ return Template._(source, ast,
+ instantiator: instantiator,
+ isExpression: isExpression,
+ forceCopy: forceCopy,
+ positionalArgumentCount: positionalArgumentCount,
+ holeNames: holeNames);
}
- Template.withExpressionResult(this.ast)
- : source = null,
- isExpression = true,
- forceCopy = false {
- assert(ast is Expression);
- assert(_checkNoPlaceholders());
- positionalArgumentCount = 0;
- instantiator = (arguments) => ast;
+ factory Template.withExpressionResult(Expression ast) {
+ assert(_checkNoPlaceholders(ast));
+ return Template._(null, ast,
+ instantiator: (arguments) => ast,
+ isExpression: true,
+ forceCopy: false,
+ positionalArgumentCount: 0);
}
- Template.withStatementResult(this.ast)
- : source = null,
- isExpression = false,
- forceCopy = false {
- assert(ast is Statement);
- assert(_checkNoPlaceholders());
- positionalArgumentCount = 0;
- instantiator = (arguments) => ast;
+ factory Template.withStatementResult(Statement ast) {
+ assert(_checkNoPlaceholders(ast));
+ return Template._(null, ast,
+ instantiator: (arguments) => ast,
+ isExpression: true,
+ forceCopy: false,
+ positionalArgumentCount: 0);
}
- bool _checkNoPlaceholders() {
+ static bool _checkNoPlaceholders(Node ast) {
var generator = InstantiatorGeneratorVisitor(false);
generator.compile(ast);
return generator.analysis.count == 0;
}
- void _compile() {
- var generator = InstantiatorGeneratorVisitor(forceCopy);
- instantiator = generator.compile(ast);
- positionalArgumentCount = generator.analysis.count;
- Set<String> names = generator.analysis.holeNames;
- holeNames = names.toList(growable: false);
- }
-
/// Instantiates the template with the given [arguments].
///
/// This method fills in the holes with the given arguments. The [arguments]
/// must be either a [List] or a [Map].
- Node instantiate(var arguments) {
+ Node instantiate(Object arguments) {
if (arguments is List) {
if (arguments.length != positionalArgumentCount) {
throw 'Wrong number of template arguments, given ${arguments.length}, '
@@ -167,7 +173,7 @@
return same<T>(node);
}
- Instantiator visitNullable<T extends Node>(T node) {
+ Instantiator visitNullable<T extends Node>(T? node) {
return node == null ? makeNull : visit(node);
}
@@ -420,7 +426,7 @@
var makeUpdate = visitNullable(node.update) as Instantiator<Expression>;
var makeBody = visit(node.body) as Instantiator<Statement>;
return (a) => For(makeInit(a), makeCondition(a),
- makeUpdate(a)?.toVoidExpression(), makeBody(a));
+ makeUpdate(a).toVoidExpression(), makeBody(a));
}
@override
@@ -463,7 +469,7 @@
@override
Instantiator<Statement> visitReturn(Return node) {
if (node.value == null) return (args) => Return();
- var makeExpression = visit(node.value) as Instantiator<Expression>;
+ var makeExpression = visit(node.value!) as Instantiator<Expression>;
return (a) => makeExpression(a).toReturn();
}
@@ -482,8 +488,8 @@
@override
Instantiator<Try> visitTry(Try node) {
var makeBody = visit(node.body) as Instantiator<Block>;
- var makeCatch = visitNullable(node.catchPart) as Instantiator<Catch>;
- var makeFinally = visitNullable(node.finallyPart) as Instantiator<Block>;
+ var makeCatch = visitNullable(node.catchPart) as Instantiator<Catch?>;
+ var makeFinally = visitNullable(node.finallyPart) as Instantiator<Block?>;
return (a) => Try(makeBody(a), makeCatch(a), makeFinally(a));
}
@@ -551,8 +557,8 @@
@override
Instantiator<Expression> visitAssignment(Assignment node) {
Instantiator makeLeftHandSide = visit(node.leftHandSide);
- String op = node.op;
- Instantiator makeValue = visitNullable(node.value);
+ String? op = node.op;
+ Instantiator makeValue = visit(node.value);
return (arguments) {
return makeValue(arguments)
.toAssignExpression(makeLeftHandSide(arguments), op) as Expression;
@@ -564,7 +570,7 @@
VariableInitialization node) {
var makeDeclaration =
visit(node.declaration) as Instantiator<VariableBinding>;
- var makeValue = visitNullable(node.value) as Instantiator<Expression>;
+ var makeValue = visitNullable(node.value) as Instantiator<Expression?>;
return (a) => VariableInitialization(makeDeclaration(a), makeValue(a));
}
@@ -741,7 +747,8 @@
Instantiator<ClassExpression> visitClassExpression(ClassExpression node) {
var makeMethods = node.methods.map(visitSplayableExpression).toList();
var makeName = visit(node.name) as Instantiator<Identifier>;
- var makeHeritage = visit(node.heritage) as Instantiator<Expression>;
+ var makeHeritage =
+ visitNullable(node.heritage) as Instantiator<Expression?>;
return (a) => ClassExpression(
makeName(a), makeHeritage(a), splayNodes(makeMethods, a));
@@ -793,12 +800,13 @@
@override
Instantiator<DestructuredVariable> visitDestructuredVariable(
DestructuredVariable node) {
- var makeName = visitNullable(node.name) as Instantiator<Identifier>;
- var makeProperty = visitNullable(node.property) as Instantiator<Expression>;
+ var makeName = visit(node.name) as Instantiator<Identifier>;
+ var makeProperty =
+ visitNullable(node.property) as Instantiator<Expression?>;
var makeStructure =
- visitNullable(node.structure) as Instantiator<BindingPattern>;
+ visitNullable(node.structure) as Instantiator<BindingPattern?>;
var makeDefaultValue =
- visitNullable(node.defaultValue) as Instantiator<Expression>;
+ visitNullable(node.defaultValue) as Instantiator<Expression?>;
return (a) => DestructuredVariable(
name: makeName(a),
property: makeProperty(a),
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 2b3db8d..81a45e4 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -15,6 +15,8 @@
show LibraryInfo;
import 'package:_fe_analyzer_shared/src/util/options.dart';
import 'package:compiler/src/kernel/dart2js_target.dart';
+import 'package:compiler/src/options.dart' as dart2jsOptions
+ show CompilerOptions;
import 'package:dev_compiler/src/kernel/target.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show
@@ -1742,7 +1744,9 @@
target = new TestTargetWrapper(new NoneTarget(targetFlags), targetFlags);
break;
case "dart2js":
- target = new TestDart2jsTarget('dart2js', targetFlags);
+ target = new TestDart2jsTarget('dart2js', targetFlags,
+ options: dart2jsOptions.CompilerOptions.parse(
+ folderOptions.defines?.values.toList() ?? []));
break;
case "dartdevc":
target = new TestDevCompilerTarget(targetFlags);
@@ -2214,7 +2218,9 @@
@override
final TestTargetFlags flags;
- TestDart2jsTarget(String name, this.flags) : super(name, flags);
+ TestDart2jsTarget(String name, this.flags,
+ {dart2jsOptions.CompilerOptions? options})
+ : super(name, flags, options: options);
}
class TestDevCompilerTarget extends DevCompilerTarget
diff --git a/pkg/front_end/testcases/general/flutter_issue64155.dart.weak.transformed.expect b/pkg/front_end/testcases/general/flutter_issue64155.dart.weak.transformed.expect
index 081530b..55e1b209 100644
--- a/pkg/front_end/testcases/general/flutter_issue64155.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/flutter_issue64155.dart.weak.transformed.expect
@@ -113,7 +113,7 @@
const synthetic constructor •() → self::_Class1&Object&TestMixin*
: super core::Object::•()
;
- method test(covariant-by-class asy::Future<self::Response<core::String*>*>* fetch) → asy::Future<core::String*>* /* originally async */ {
+ method test(covariant-by-class asy::Future<self::Response<core::String*>*>* fetch) → asy::Future<core::String*>* /* futureValueType= core::String* */ /* originally async */ {
final asy::_Future<core::String*>* :async_future = new asy::_Future::•<core::String*>();
core::bool* :is_sync = false;
core::String? :return_value;
@@ -182,7 +182,7 @@
const synthetic constructor •() → self::_Class2&Object&TestMixin*
: super core::Object::•()
;
- method test(covariant-by-class asy::Future<self::PagingResponse<core::String*>*>* fetch) → asy::Future<core::String*>* /* originally async */ {
+ method test(covariant-by-class asy::Future<self::PagingResponse<core::String*>*>* fetch) → asy::Future<core::String*>* /* futureValueType= core::String* */ /* originally async */ {
final asy::_Future<core::String*>* :async_future = new asy::_Future::•<core::String*>();
core::bool* :is_sync = false;
core::String? :return_value;
diff --git a/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.strong.transformed.expect
index dd7aa66..5244f7f 100644
--- a/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.strong.transformed.expect
@@ -73,7 +73,7 @@
const synthetic constructor •() → self::_Class1&Object&TestMixin
: super core::Object::•()
;
- method test(covariant-by-class asy::Future<self::Response<core::String>> fetch) → asy::Future<core::String> /* originally async */ {
+ method test(covariant-by-class asy::Future<self::Response<core::String>> fetch) → asy::Future<core::String> /* futureValueType= core::String */ /* originally async */ {
final asy::_Future<core::String> :async_future = new asy::_Future::•<core::String>();
core::bool* :is_sync = false;
core::String? :return_value;
@@ -132,7 +132,7 @@
const synthetic constructor •() → self::_Class2&Object&TestMixin
: super core::Object::•()
;
- method test(covariant-by-class asy::Future<self::PagingResponse<core::String>> fetch) → asy::Future<core::String> /* originally async */ {
+ method test(covariant-by-class asy::Future<self::PagingResponse<core::String>> fetch) → asy::Future<core::String> /* futureValueType= core::String */ /* originally async */ {
final asy::_Future<core::String> :async_future = new asy::_Future::•<core::String>();
core::bool* :is_sync = false;
core::String? :return_value;
diff --git a/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.weak.transformed.expect
index dd7aa66..5244f7f 100644
--- a/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/flutter_issue64155.dart.weak.transformed.expect
@@ -73,7 +73,7 @@
const synthetic constructor •() → self::_Class1&Object&TestMixin
: super core::Object::•()
;
- method test(covariant-by-class asy::Future<self::Response<core::String>> fetch) → asy::Future<core::String> /* originally async */ {
+ method test(covariant-by-class asy::Future<self::Response<core::String>> fetch) → asy::Future<core::String> /* futureValueType= core::String */ /* originally async */ {
final asy::_Future<core::String> :async_future = new asy::_Future::•<core::String>();
core::bool* :is_sync = false;
core::String? :return_value;
@@ -132,7 +132,7 @@
const synthetic constructor •() → self::_Class2&Object&TestMixin
: super core::Object::•()
;
- method test(covariant-by-class asy::Future<self::PagingResponse<core::String>> fetch) → asy::Future<core::String> /* originally async */ {
+ method test(covariant-by-class asy::Future<self::PagingResponse<core::String>> fetch) → asy::Future<core::String> /* futureValueType= core::String */ /* originally async */ {
final asy::_Future<core::String> :async_future = new asy::_Future::•<core::String>();
core::bool* :is_sync = false;
core::String? :return_value;
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 43d7c32..c69fe09 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -594,6 +594,8 @@
List<VariableDeclaration> positional =
node.positionalParameters.map(clone).toList();
List<VariableDeclaration> named = node.namedParameters.map(clone).toList();
+ final DartType? futureValueType =
+ node.futureValueType != null ? visitType(node.futureValueType!) : null;
return new FunctionNode(cloneFunctionNodeBody(node),
typeParameters: typeParameters,
positionalParameters: positional,
@@ -601,7 +603,8 @@
requiredParameterCount: node.requiredParameterCount,
returnType: visitType(node.returnType),
asyncMarker: node.asyncMarker,
- dartAsyncMarker: node.dartAsyncMarker)
+ dartAsyncMarker: node.dartAsyncMarker,
+ futureValueType: futureValueType)
..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
}
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index 2cce01c..f30a460 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -203,6 +203,8 @@
"-ggdb3",
"-fno-rtti",
"-fno-exceptions",
+ "-fno-strict-vtable-pointers", # Handle assignment updates vtable
+ # pointers.
]
if (is_clang) {
cflags += [ "-Wimplicit-fallthrough" ]
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 008767b..ac28ae3 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -659,8 +659,21 @@
return obj;
}
- cpp_vtable vtable() const { return bit_copy<cpp_vtable>(*this); }
- void set_vtable(cpp_vtable value) { *vtable_address() = value; }
+ // Memcpy to account for the strict aliasing rule.
+ // Explicit cast to silence -Wdynamic-class-memaccess.
+ // This is still undefined behavior because we're messing with the internal
+ // representation of C++ objects, but works okay in practice with
+ // -fno-strict-vtable-pointers.
+ cpp_vtable vtable() const {
+ cpp_vtable result;
+ memcpy(&result, reinterpret_cast<const void*>(this), // NOLINT
+ sizeof(result));
+ return result;
+ }
+ void set_vtable(cpp_vtable value) {
+ memcpy(reinterpret_cast<void*>(this), &value, // NOLINT
+ sizeof(cpp_vtable));
+ }
static ObjectPtr Allocate(intptr_t cls_id,
intptr_t size,
@@ -803,11 +816,6 @@
obj->SetPtr(ptr, kObjectCid);
}
- cpp_vtable* vtable_address() const {
- uword vtable_addr = reinterpret_cast<uword>(this);
- return reinterpret_cast<cpp_vtable*>(vtable_addr);
- }
-
static cpp_vtable builtin_vtables_[kNumPredefinedCids];
// The static values below are singletons shared between the different
diff --git a/tools/VERSION b/tools/VERSION
index 08e93be..3bced45 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 254
+PRERELEASE 255
PRERELEASE_PATCH 0
\ No newline at end of file