Skip to content

Commit 9f127c9

Browse files
srawlinsCommit Queue
authored andcommitted
analyzer: Tidy strict-inference-in-parameters check
The existing code had two local functions which were an unnecessary structuring of the check. The story might be hard to read in the diff. 1. The previous method body for `_checkStrictInferenceInParameters` first declared a variable `usedParameterVisitor` which may or not be ever used, and then created two local functions. 2. The first function, `isParameterReferenced`, is only called from the second function. It has a lot of complexity because it is responsible for maybe instantiating that local variable, only once, to cache the results of visiting the function body. 3. The second function is called twice, which is why it was nice to encapsulate it in a function. 4. _After_ those things are declared, we check if we're even using `strict-inference`, or that the parameter list is null. This results in many null-asserts on the parameter list. 5. The code that extracts out a NormalFormalParameter from a DefaultFormalParameter is a little complicated. The changes sort of unwind the complexity from the bottom up: * We can move the `strict-inference` check and the parameter list null- check to the tippy top, returning early (4). This removes many null-asserts. * We can simplify the code that extracts the NormalFormalParameter from any DefaultFormalParameter (5), which means there would only be one call to `checkParameterTypeIsKnown`. With only one call site, the encapsulation is silly (3), and we can inline the function, removing `checkParameterTypeIsKnown`. * Most of the complexity of the first function, `isParameterReferenced` is around initializing this expensive visitor, so that we only visit the function body once. But it turns out, we only do that if we cannot know whether the parameters are used, because `initializers` is null, and the `body` is null or empty. But none of that knowledge is per- parameter, so it can be computed outside this function. So we introduce `parameterReferenceIsUnknown`. We never need to call `isParameterReferenced` if `parameterReferenceIsUnknown`. Or, put another way, we only need to call `isParameterReferenced` if we know we need that visitor. So we can now computer the visitor outside the function, after which the function is so simple (a single returned expression), it can be removed. All in all, here are the broad simplifications: * We return early in a few common scenarios, in which case we neither declare that visitor variable, nor declare the local functions. * We compute `parameterReferenceIsUnknown` once, not per parameter. * We simplify how inner `NormalFormalParameters` are extracted. * We simplify instantiating the visitor, rather than in a local function. * We inline simple local functions. Change-Id: I308f328940f809f10cad884e68f064dd0a468a95 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442280 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 6610e5c commit 9f127c9

File tree

1 file changed

+40
-44
lines changed

1 file changed

+40
-44
lines changed

pkg/analyzer/lib/src/error/best_practices_verifier.dart

Lines changed: 40 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,65 +1428,61 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
14281428
}
14291429
}
14301430

1431-
/// In "strict-inference" mode, check that each of the [parameters]' type is
1432-
/// specified.
1431+
/// In "strict-inference" mode, check that each of the [parameterList]' type
1432+
/// is specified.
14331433
///
14341434
/// Only parameters which are referenced in [initializers] or [body] are
14351435
/// reported. If [initializers] and [body] are both null, the parameters are
14361436
/// assumed to originate from a typedef, function-typed parameter, or function
14371437
/// which is abstract or external.
14381438
void _checkStrictInferenceInParameters(
1439-
FormalParameterList? parameters, {
1439+
FormalParameterList? parameterList, {
14401440
List<ConstructorInitializer>? initializers,
14411441
FunctionBody? body,
14421442
}) {
1443-
_UsedParameterVisitor? usedParameterVisitor;
1443+
if (!_strictInference || parameterList == null) {
1444+
return;
1445+
}
14441446

1445-
bool isParameterReferenced(SimpleFormalParameter parameter) {
1446-
if ((body == null || body is EmptyFunctionBody) && initializers == null) {
1447-
// The parameter is in a typedef, or function that is abstract,
1448-
// external, etc.
1449-
return true;
1450-
}
1451-
if (usedParameterVisitor == null) {
1452-
// Visit the function body and initializers once to determine whether
1453-
// each of the parameters is referenced.
1454-
usedParameterVisitor = _UsedParameterVisitor(
1455-
parameters!.parameters
1456-
.map((p) => p.declaredFragment!.element)
1457-
.toSet(),
1458-
);
1459-
body?.accept(usedParameterVisitor!);
1460-
for (var initializer in initializers ?? <ConstructorInitializer>[]) {
1461-
initializer.accept(usedParameterVisitor!);
1462-
}
1463-
}
1447+
var implicitlyTypedParameters =
1448+
parameterList.parameters
1449+
.map((p) => p.notDefault)
1450+
.whereType<SimpleFormalParameter>()
1451+
.where((p) => p.type == null)
1452+
.toList();
14641453

1465-
return usedParameterVisitor!.isUsed(parameter.declaredFragment!.element);
1466-
}
1454+
if (implicitlyTypedParameters.isEmpty) return;
14671455

1468-
void checkParameterTypeIsKnown(SimpleFormalParameter parameter) {
1469-
if (parameter.type == null && isParameterReferenced(parameter)) {
1470-
var element = parameter.declaredFragment!.element;
1471-
_diagnosticReporter.atNode(
1472-
parameter,
1473-
WarningCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER,
1474-
arguments: [element.displayName],
1475-
);
1476-
}
1477-
}
1456+
// Whether the parameters are in a typedef, or function that is abstract,
1457+
// external, etc.
1458+
var parameterReferenceIsUnknown =
1459+
(body == null || body is EmptyFunctionBody) && initializers == null;
14781460

1479-
if (_strictInference && parameters != null) {
1480-
for (FormalParameter parameter in parameters.parameters) {
1481-
if (parameter is SimpleFormalParameter) {
1482-
checkParameterTypeIsKnown(parameter);
1483-
} else if (parameter is DefaultFormalParameter) {
1484-
var nonDefault = parameter.parameter;
1485-
if (nonDefault is SimpleFormalParameter) {
1486-
checkParameterTypeIsKnown(nonDefault);
1487-
}
1461+
if (!parameterReferenceIsUnknown) {
1462+
var usedVisitor = _UsedParameterVisitor(
1463+
implicitlyTypedParameters
1464+
.map((p) => p.declaredFragment!.element)
1465+
.toSet(),
1466+
);
1467+
body?.accept(usedVisitor);
1468+
if (initializers != null) {
1469+
for (var initializer in initializers) {
1470+
initializer.accept(usedVisitor);
14881471
}
14891472
}
1473+
1474+
implicitlyTypedParameters.removeWhere(
1475+
(p) => !usedVisitor.isUsed(p.declaredFragment!.element),
1476+
);
1477+
}
1478+
1479+
for (var parameter in implicitlyTypedParameters) {
1480+
var element = parameter.declaredFragment!.element;
1481+
_diagnosticReporter.atNode(
1482+
parameter,
1483+
WarningCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER,
1484+
arguments: [element.displayName],
1485+
);
14901486
}
14911487
}
14921488

0 commit comments

Comments
 (0)