Skip to content

Commit

Permalink
Version 3.8.0-95.0.dev
Browse files Browse the repository at this point in the history
Merge 1a0b1fe into dev
  • Loading branch information
Dart CI committed Feb 13, 2025
2 parents a389ef1 + 1a0b1fe commit efd2ea5
Show file tree
Hide file tree
Showing 74 changed files with 1,647 additions and 803 deletions.
4 changes: 2 additions & 2 deletions DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ vars = {
# 'tools/rev_sdk_deps.dart' can rev pkg dependencies to their latest; put an
# EOL comment after a dependency to disable this and pin it at its current
# revision.
"core_rev": "a896913715f2d8fb86d099b75dbfd02bf0d658a4",
"core_rev": "0b2bd3fcd7f3e082f4cc9b14c19ffa93894b85ae",
"dartdoc_rev": "e1295863b11c54680bf178ec9c2662a33b0e24be", # https://github.com/dart-lang/dartdoc/issues/3969
"ecosystem_rev": "5b0d815952d81f5a51cc6a1af1cacab26f3a61d7",
"flute_rev": "e4ea0459a7debae5e9592c85141707b01fac86c9",
Expand All @@ -148,7 +148,7 @@ vars = {
"sync_http_rev": "47e6b264a209d0d806cfe9cdad8b6c69ce231986",
"tar_rev": "5a1ea943e70cdf3fa5e1102cdbb9418bd9b4b81a",
"test_rev": "17609bf90c9d5ef47707f5796763629450382474",
"tools_rev": "30de295e959beaaae7a18d2249a1cddb9053dd02", # b/391934702
"tools_rev": "750b4ad8ea80dd0bfa1d4dcf05915183c2bf6dab", # b/391934702
"vector_math_rev": "533c513771d35312dcd0f69e662d729979882df1",
"web_rev": "c2d5f63e9ea4c1409d6e159fc7b92dbcf4dc0d4d",
"web_socket_channel_rev": "f335e52affa5c70a725401394bf265fc4384e62f",
Expand Down
1 change: 1 addition & 0 deletions pkg/analysis_server/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"name": "Current file",
"request": "launch",
"type": "dart",
"program": "${file}",
},
{
"name": "test: all",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,23 @@ class MoveTopLevelToFile extends RefactoringProducer {
var library = entry.key;
var prefixes = <String>{};
for (var element in entry.value) {
var prefixList = await searchEngine.searchPrefixesUsedInLibrary(
library,
element,
// Search for prefixes for the element.
prefixes.addAll(
await searchEngine.searchPrefixesUsedInLibrary(library, element),
);
prefixes.addAll(prefixList);
// And also for the getter if this might be something like a top-level
// variable.
if (element case PropertyInducingElement2(:var getter2?)) {
prefixes.addAll(
await searchEngine.searchPrefixesUsedInLibrary(library, getter2),
);
}
// And setters.
if (element case PropertyInducingElement2(:var setter2?)) {
prefixes.addAll(
await searchEngine.searchPrefixesUsedInLibrary(library, setter2),
);
}
}
await builder.addDartFileEdit(library.firstFragment.source.fullName, (
builder,
Expand Down
25 changes: 25 additions & 0 deletions pkg/analysis_server/test/lsp/definition_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,31 @@ class A {
await testContents(contents);
}

Future<void> test_closure_parameter() async {
setLocationLinkSupport();

var code = TestCode.parse('''
void f(void Function(int) _) {}
void g() => f((/*[0*/variable/*0]*/) {
print(/*[1*/^variable/*1]*/);
});
''');

await initialize();
await openFile(mainFileUri, code.code);
var res = await getDefinitionAsLocationLinks(
mainFileUri,
code.position.position,
);

expect(res, hasLength(1));
var loc = res.first;
expect(loc.originSelectionRange, equals(code.ranges.last.range));
expect(loc.targetRange, equals(code.ranges.first.range));
expect(loc.targetSelectionRange, equals(code.ranges.first.range));
}

Future<void> test_comment_adjacentReference() async {
/// Computing Dart navigation locates a node at the provided offset then
/// returns all navigation regions inside it. This test ensures we filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,29 @@ void f(test) {
assertRefactoringStatusOK(refactoring.checkNewName());
}

Future<void> test_createChange_closure_parameter() async {
await indexTestUnit('''
void f(void Function(int) _) {}
void g() => f((parameter) {
print(parameter);
});
''');
// configure refactoring
createRenameRefactoringAtString('parameter) {');
expect(refactoring.refactoringName, 'Rename Parameter');
expect(refactoring.elementKindName, 'parameter');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
void f(void Function(int) _) {}
void g() => f((newName) {
print(newName);
});
''');
}

Future<void> test_createChange_localFunction() async {
await indexTestUnit('''
void f() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,53 @@ part of 'containing_library.dart';
);
}

/// https://github.com/dart-lang/sdk/issues/59968#issuecomment-2622191812
Future<void> test_single_topLevelVariable_withReferenceToGetter() async {
var originalSource = '''
class A {}
int variableT^oMove = 3;
class B {}
''';
var otherFilePath = '$projectFolderPath/lib/other.dart';
var otherFileContent = '''
import "main.dart";
void f() {
print(variableToMove);
}
''';

var declarationName = 'variableToMove';

var expected = '''
>>>>>>>>>> lib/main.dart
class A {}
class B {}
>>>>>>>>>> lib/other.dart
import "package:test/variable_to_move.dart";
import "main.dart";
void f() {
print(variableToMove);
}
>>>>>>>>>> lib/variable_to_move.dart created
int variableToMove = 3;
''';

await _singleDeclaration(
originalSource: originalSource,
expected: expected,
declarationName: declarationName,
otherFilePath: otherFilePath,
otherFileContent: otherFileContent,
);
}

Future<void> test_single_typedef() async {
var originalSource = '''
class A {}
Expand Down
147 changes: 147 additions & 0 deletions pkg/analysis_server_plugin/doc/writing_rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Writing Rules

This package gives analyzer plugin authors the ability to write static rules
for source code. This document describes briefly how to write such a rule, and
how to register it in an analyzer plugin.

## Declaring an analysis rule

Every analysis rule is declared in two parts: a rule class that extends
`AnalysisRule`, and a visitor class that extends `SimpleAstVisitor`.

### The rule class

The rule class contains some general information about the rule, like its name
and the diagnostic or diagnostics that the rule reports. It also registers the
various syntax tree nodes that the visitor class needs to visit. Let's see an
example:

```dart
class MyRule extends AnalysisRule {
static const LintCode _code = LintCode(
'my_rule',
'No await expressions',
correctionMessage: "Try removing 'await'.",
);
MyRule()
: super(
name: LintNames.prefer_void_to_null,
description: 'A longer description of the rule.',
);
@override
LintCode get lintCode => _code;
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this, context);
registry.addAwaitExpression(this, visitor);
}
}
```

Let's look at each declaration individually:

* `class MyRule extends AnalysisRule` - The rule class must extend
`AnalysisRule`.

* `static const LintCode _code` and `LintCode get lintCode` - Each rule class
must implement either `LintCode get lintCode` or `List<LintCode> get
lintCodes`, depending on whether there is only one diagnostic that it can
report, or multiple.

A `LintCode` is the template for each diagnostic that is to be reported. It
contains the diagnostic name, problem message, and optionally the correction
message. We instantiate a `LintCode` as a static field so that it can also be
made const. If the rule needs to report more than one `LintCode`, with
different problem messages, then multiple static fields can be declared.

* `MyRule()` - The rule class must have a constructor that calls `super()`,
passing along the name of the rule, and a description. Typically this
constructor has zero parameters.

* `void registerNodeProcessors(...)` - An analysis rule uses a visitor to walk
a [Dart syntax tree][] (we see how the visitor is defined in "The visitor
class," below). This visitor is typically named `_Visitor`. This visitor
class must be instantiated once in this method. Typically, the instance of
the rule class (`this`) and a `LinterContext` object (described below) are
passed to the visitor constructor.

In order for such a visitor's various 'visit' methods to be called, we need
to register them, in a `NodeLintRegistry`. Each 'visit' method found on
`SimpleAstVisitor` has a corresponding 'add' method in the `NodeLintRegistry`
class.

[Dart syntax tree]: https://github.com/dart-lang/sdk/blob/main/pkg/analyzer/doc/tutorial/ast.md

### The visitor class

The visitor class contains the code that examines syntax nodes and reports
diagnostics. See the [API documentation][SimpleAstVisitor docs] for the
`SimpleAstVisitor` class to find the various 'visit' methods available for
implementation. Let's look at a quick example:

[SimpleAstVisitor docs]: https://github.com/dart-lang/sdk/blob/main/pkg/analyzer/lib/dart/ast/visitor.dart#L1841

```dart
class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;
final LinterContext context;
_Visitor(this.rule, this.context);
@override
void visitAwaitExpression(AwaitExpression node) {
if (context.isInLibDir) {
rule.reportLint(node);
}
}
}
```

Let's look at each declaration individually:

* `class _Visitor extends SimpleAstVisitor<void>` - Each visitor must extend
`SimpleAstVisitor`. While the analyzer package provides other Dart syntax
tree visitors, using one directly in a rule can result in poor performance
and unexpected behavior. The type argument on `SimpleAstVisitor` is not
important, as 'visit' return values are not used, so `void` is appropriate.
* `final LintRule rule` - The rule is the object to which we can report
diagnostics (lints or warnings). Several methods are provided, all starting
with `reportLint`. The different methods allow for different ranges of text
to be highlighted.
* `final LinterContext context` - The LinterContext object provides various
information about the library being analyzed. In this example, we make use of
a `isInLibDir` utility.
* `_Visitor(...)` - Often the constructor just initializes the LintRule and
LinterContext fields. Other information can be initialized as well.
* `void visitAwaitExpression(AwaitExpression node)` - The main component of the
`_Visitor` class is the 'visit' methods. In this case, `visitAwaitExpression`
is invoked for each 'await expression' found in the source code under
analysis. Typically, a 'visit' method like this is where we perform some
analysis and maybe report lint(s) or warning(s).

## Registering an analysis rule

In order for an analysis rule to be used in an analyzer plugin, it must be
registered. Register an instance of an analysis rule inside a plugin's
`register` method:

```dart
class SimplePlugin extends Plugin {
@override
void register(PluginRegistry registry) {
registry.registerWarningRule(MyRule());
}
}
```

Here, the instance of MyRule is registered as a "warning rule," so that it is
enabled by default. To register an analysis rule as a "lint rule," such that it
must be specifically enabled from analysis options, use `registerLintRule`
instead.

TODO(srawlins): Write up and link documentation for this Plugin subclass.
6 changes: 4 additions & 2 deletions pkg/analyzer/lib/src/dart/analysis/search.dart
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,10 @@ class Search {
if (index != null) {
_IndexRequest request = _IndexRequest(index);
int elementId = request.findElementId(element.asElement!);
var prefixList = index.elementImportPrefixes[elementId].split(',');
prefixes.addAll(prefixList);
if (elementId != -1) {
var prefixList = index.elementImportPrefixes[elementId].split(',');
prefixes.addAll(prefixList);
}
}
}
return prefixes;
Expand Down
37 changes: 12 additions & 25 deletions pkg/analyzer/lib/src/dart/analysis/session_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@
// 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.

// ignore_for_file: analyzer_use_new_elements

import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';

/// A wrapper around [AnalysisSession] that provides additional utilities.
///
Expand Down Expand Up @@ -58,7 +54,7 @@ class AnalysisSessionHelper {
Future<EnumElement2?> getEnum(String libraryUri, String className) async {
var libraryResult = await session.getLibraryByUri(libraryUri);
if (libraryResult is LibraryElementResult) {
var element = libraryResult.element.exportNamespace.get2(className);
var element = libraryResult.element2.exportNamespace.get2(className);
if (element is EnumElement2) {
return element;
}
Expand Down Expand Up @@ -86,12 +82,17 @@ class AnalysisSessionHelper {
return null;
}

/// Return the resolved unit that declares the given [element2].
Future<ResolvedUnitResult?> getResolvedUnitByElement(
Element2 element2) async {
var element = element2.asElement;
if (element == null) return null;
return await _getResolvedUnitByElement(element);
/// Returns the resolved unit that declares the given [element].
Future<ResolvedUnitResult?> getResolvedUnitByElement(Element2 element) async {
var libraryPath = element.library2!.firstFragment.source.fullName;
var resolvedLibrary = await _getResolvedLibrary(libraryPath);
if (resolvedLibrary == null) {
return null;
}

var unitPath = element.firstFragment.libraryFragment!.source.fullName;
return resolvedLibrary.units
.singleWhere((resolvedUnit) => resolvedUnit.path == unitPath);
}

/// Returns the [PropertyAccessorElement2] with the given [name] that is
Expand Down Expand Up @@ -122,18 +123,4 @@ class AnalysisSessionHelper {
}
return result;
}

/// Return the resolved unit that declares the given [element].
Future<ResolvedUnitResult?> _getResolvedUnitByElement(Element element) async {
var libraryPath = element.library!.source.fullName;
var resolvedLibrary = await _getResolvedLibrary(libraryPath);
if (resolvedLibrary == null) {
return null;
}

var unitPath = element.source!.fullName;
return resolvedLibrary.units.singleWhere((resolvedUnit) {
return resolvedUnit.path == unitPath;
});
}
}
Loading

0 comments on commit efd2ea5

Please sign in to comment.