Skip to content

Commit 8aa0bf5

Browse files
srawlinsCommit Queue
authored andcommitted
analyzer: move constant-evaluation lint utilities to their own library
These utilities are distinct from the base classes like AnalysisRule and LinterContext. And their import requirements are also distinct. Separating them in the filesystem should simplify how we slice up the public API. Change-Id: I365c48c41e5d3db1e3aa649fd70b94cd7da1b069 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395561 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 1bbfe85 commit 8aa0bf5

17 files changed

+235
-225
lines changed

pkg/analysis_server/lib/src/computer/computer_color.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:analyzer/dart/constant/value.dart';
99
import 'package:analyzer/dart/element/element2.dart';
1010
import 'package:analyzer/src/dart/ast/ast.dart';
1111
import 'package:analyzer/src/dart/constant/value.dart' show GenericState;
12-
import 'package:analyzer/src/lint/linter.dart';
12+
import 'package:analyzer/src/lint/constants.dart';
1313
import 'package:analyzer/src/utilities/extensions/flutter.dart';
1414
import 'package:collection/collection.dart';
1515
import 'package:path/path.dart' as path;

pkg/analysis_server/lib/src/lsp/handlers/custom/handler_editable_arguments.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import 'package:analyzer/dart/element/nullability_suffix.dart';
1616
import 'package:analyzer/dart/element/type.dart';
1717
import 'package:analyzer/src/dart/ast/ast.dart';
1818
import 'package:analyzer/src/dart/element/element.dart';
19-
import 'package:analyzer/src/lint/linter.dart';
19+
import 'package:analyzer/src/lint/constants.dart';
2020
import 'package:analyzer/src/utilities/extensions/ast.dart';
2121
import 'package:analyzer/src/utilities/extensions/flutter.dart';
2222

pkg/analysis_server/lib/src/services/correction/dart/add_const.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:analyzer/dart/ast/ast.dart';
99
import 'package:analyzer/dart/ast/token.dart';
1010
import 'package:analyzer/dart/ast/visitor.dart';
1111
import 'package:analyzer/source/source_range.dart';
12-
import 'package:analyzer/src/lint/linter.dart';
12+
import 'package:analyzer/src/lint/constants.dart';
1313
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_dart.dart';
1414
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
1515
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';

pkg/analysis_server/lib/src/services/correction/dart/replace_with_decorated_box.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
77
import 'package:analyzer/dart/ast/ast.dart';
88
import 'package:analyzer/dart/ast/token.dart';
99
import 'package:analyzer/error/error.dart';
10-
import 'package:analyzer/src/lint/linter.dart';
10+
import 'package:analyzer/src/lint/constants.dart';
1111
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
1212
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
1313
import 'package:analyzer_plugin/utilities/range_factory.dart';

pkg/analyzer/analyzer_use_new_elements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ lib/src/generated/static_type_analyzer.dart
122122
lib/src/generated/testing/element_factory.dart
123123
lib/src/generated/testing/test_type_provider.dart
124124
lib/src/hint/sdk_constraint_verifier.dart
125+
lib/src/lint/constants.dart
125126
lib/src/lint/linter.dart
126127
lib/src/services/top_level_declarations.dart
127128
lib/src/summary2/ast_binary_reader.dart

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import 'package:analyzer/src/error/doc_comment_verifier.dart';
3434
import 'package:analyzer/src/error/error_handler_verifier.dart';
3535
import 'package:analyzer/src/error/must_call_super_verifier.dart';
3636
import 'package:analyzer/src/error/null_safe_api_verifier.dart';
37-
import 'package:analyzer/src/lint/linter.dart';
37+
import 'package:analyzer/src/lint/constants.dart';
3838
import 'package:analyzer/src/utilities/extensions/ast.dart';
3939
import 'package:analyzer/src/utilities/extensions/element.dart';
4040
import 'package:analyzer/src/workspace/workspace.dart';
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/ast/token.dart';
6+
import 'package:analyzer/dart/constant/value.dart';
7+
import 'package:analyzer/dart/element/element.dart';
8+
import 'package:analyzer/error/error.dart';
9+
import 'package:analyzer/error/listener.dart';
10+
import 'package:analyzer/src/dart/ast/ast.dart';
11+
import 'package:analyzer/src/dart/ast/token.dart';
12+
import 'package:analyzer/src/dart/constant/compute.dart';
13+
import 'package:analyzer/src/dart/constant/constant_verifier.dart';
14+
import 'package:analyzer/src/dart/constant/evaluation.dart';
15+
import 'package:analyzer/src/dart/constant/potentially_constant.dart';
16+
import 'package:analyzer/src/dart/constant/utilities.dart';
17+
import 'package:analyzer/src/dart/constant/value.dart';
18+
import 'package:analyzer/src/dart/element/element.dart';
19+
import 'package:analyzer/src/error/codes.dart';
20+
21+
/// The result of attempting to evaluate an expression as a constant.
22+
final class LinterConstantEvaluationResult {
23+
/// The value of the expression, or `null` if has [errors].
24+
final DartObject? value;
25+
26+
/// The errors reported during the evaluation.
27+
final List<AnalysisError> errors;
28+
29+
LinterConstantEvaluationResult._(this.value, this.errors);
30+
}
31+
32+
/// An error listener that only records whether any constant related errors have
33+
/// been reported.
34+
class _ConstantAnalysisErrorListener extends AnalysisErrorListener {
35+
/// A flag indicating whether any constant related errors have been reported
36+
/// to this listener.
37+
bool hasConstError = false;
38+
39+
@override
40+
void onError(AnalysisError error) {
41+
ErrorCode errorCode = error.errorCode;
42+
if (errorCode is CompileTimeErrorCode) {
43+
switch (errorCode) {
44+
case CompileTimeErrorCode
45+
.CONST_CONSTRUCTOR_CONSTANT_FROM_DEFERRED_LIBRARY:
46+
case CompileTimeErrorCode
47+
.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST:
48+
case CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD:
49+
case CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD:
50+
case CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION:
51+
case CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS:
52+
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL:
53+
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_INT:
54+
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING:
55+
case CompileTimeErrorCode.CONST_EVAL_TYPE_INT:
56+
case CompileTimeErrorCode.CONST_EVAL_TYPE_NUM:
57+
case CompileTimeErrorCode.CONST_EVAL_TYPE_NUM_STRING:
58+
case CompileTimeErrorCode.CONST_EVAL_TYPE_STRING:
59+
case CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION:
60+
case CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE:
61+
case CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT:
62+
case CompileTimeErrorCode.CONST_MAP_KEY_NOT_PRIMITIVE_EQUALITY:
63+
case CompileTimeErrorCode.CONST_SET_ELEMENT_NOT_PRIMITIVE_EQUALITY:
64+
case CompileTimeErrorCode.CONST_TYPE_PARAMETER:
65+
case CompileTimeErrorCode.CONST_WITH_NON_CONST:
66+
case CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT:
67+
case CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS:
68+
case CompileTimeErrorCode
69+
.CONST_WITH_TYPE_PARAMETERS_CONSTRUCTOR_TEAROFF:
70+
case CompileTimeErrorCode.INVALID_CONSTANT:
71+
case CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL:
72+
case CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL:
73+
case CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL:
74+
case CompileTimeErrorCode.NON_BOOL_CONDITION:
75+
case CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT:
76+
case CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT:
77+
case CompileTimeErrorCode.NON_CONSTANT_MAP_KEY:
78+
case CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE:
79+
case CompileTimeErrorCode.NON_CONSTANT_RECORD_FIELD:
80+
case CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT:
81+
hasConstError = true;
82+
}
83+
}
84+
}
85+
}
86+
87+
extension on AstNode {
88+
/// Whether [ConstantVerifier] reports an error when computing the value of
89+
/// `this` as a constant.
90+
bool get hasConstantVerifierError {
91+
var unitElement = thisOrAncestorOfType<CompilationUnit>()?.declaredElement;
92+
if (unitElement == null) return false;
93+
var libraryElement = unitElement.library as LibraryElementImpl;
94+
95+
var dependenciesFinder = ConstantExpressionsDependenciesFinder();
96+
accept(dependenciesFinder);
97+
computeConstants(
98+
declaredVariables: unitElement.session.declaredVariables,
99+
constants: dependenciesFinder.dependencies.toList(),
100+
featureSet: libraryElement.featureSet,
101+
configuration: ConstantEvaluationConfiguration(),
102+
);
103+
104+
var listener = _ConstantAnalysisErrorListener();
105+
var errorReporter = ErrorReporter(listener, unitElement.source);
106+
107+
accept(
108+
ConstantVerifier(
109+
errorReporter,
110+
libraryElement,
111+
unitElement.session.declaredVariables,
112+
),
113+
);
114+
return listener.hasConstError;
115+
}
116+
}
117+
118+
extension ConstructorDeclarationExtension on ConstructorDeclaration {
119+
bool get canBeConst {
120+
var element = declaredElement!;
121+
122+
var classElement = element.enclosingElement3;
123+
if (classElement is ClassElement && classElement.hasNonFinalField) {
124+
return false;
125+
}
126+
127+
var oldKeyword = constKeyword;
128+
var self = this as ConstructorDeclarationImpl;
129+
try {
130+
temporaryConstConstructorElements[element] = true;
131+
self.constKeyword = KeywordToken(Keyword.CONST, offset);
132+
return !hasConstantVerifierError;
133+
} finally {
134+
temporaryConstConstructorElements[element] = null;
135+
self.constKeyword = oldKeyword;
136+
}
137+
}
138+
}
139+
140+
extension ExpressionExtension on Expression {
141+
/// Whether it would be valid for this expression to have a `const` keyword.
142+
///
143+
/// Note that this method can cause constant evaluation to occur, which can be
144+
/// computationally expensive.
145+
bool get canBeConst {
146+
var self = this;
147+
return switch (self) {
148+
InstanceCreationExpressionImpl() => _canBeConstInstanceCreation(self),
149+
TypedLiteralImpl() => _canBeConstTypedLiteral(self),
150+
_ => false,
151+
};
152+
}
153+
154+
/// Computes the constant value of `this`, if it has one.
155+
///
156+
/// Returns a [LinterConstantEvaluationResult], containing both the computed
157+
/// constant value, and a list of errors that occurred during the computation.
158+
LinterConstantEvaluationResult computeConstantValue() {
159+
var unitElement = thisOrAncestorOfType<CompilationUnit>()?.declaredElement;
160+
if (unitElement == null) return LinterConstantEvaluationResult._(null, []);
161+
var libraryElement = unitElement.library as LibraryElementImpl;
162+
163+
var errorListener = RecordingErrorListener();
164+
165+
var evaluationEngine = ConstantEvaluationEngine(
166+
declaredVariables: unitElement.session.declaredVariables,
167+
configuration: ConstantEvaluationConfiguration(),
168+
);
169+
170+
var dependencies = <ConstantEvaluationTarget>[];
171+
accept(ReferenceFinder(dependencies.add));
172+
173+
computeConstants(
174+
declaredVariables: unitElement.session.declaredVariables,
175+
constants: dependencies,
176+
featureSet: libraryElement.featureSet,
177+
configuration: ConstantEvaluationConfiguration(),
178+
);
179+
180+
var visitor = ConstantVisitor(
181+
evaluationEngine,
182+
libraryElement,
183+
ErrorReporter(errorListener, unitElement.source),
184+
);
185+
186+
var constant = visitor.evaluateAndReportInvalidConstant(this);
187+
var dartObject = constant is DartObjectImpl ? constant : null;
188+
return LinterConstantEvaluationResult._(dartObject, errorListener.errors);
189+
}
190+
191+
bool _canBeConstInstanceCreation(InstanceCreationExpressionImpl node) {
192+
var element = node.constructorName.staticElement;
193+
if (element == null || !element.isConst) return false;
194+
195+
// Ensure that dependencies (e.g. default parameter values) are computed.
196+
var implElement = element.declaration as ConstructorElementImpl;
197+
implElement.computeConstantDependencies();
198+
199+
// Verify that the evaluation of the constructor would not produce an
200+
// exception.
201+
var oldKeyword = node.keyword;
202+
try {
203+
node.keyword = KeywordToken(Keyword.CONST, offset);
204+
return !hasConstantVerifierError;
205+
} finally {
206+
node.keyword = oldKeyword;
207+
}
208+
}
209+
210+
bool _canBeConstTypedLiteral(TypedLiteralImpl node) {
211+
var oldKeyword = node.constKeyword;
212+
try {
213+
node.constKeyword = KeywordToken(Keyword.CONST, offset);
214+
return !hasConstantVerifierError;
215+
} finally {
216+
node.constKeyword = oldKeyword;
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)