@@ -19,21 +19,31 @@ import 'package:analyzer/dart/ast/doc_comment.dart';
1919import 'package:analyzer/dart/ast/precedence.dart';
2020import 'package:analyzer/dart/ast/syntactic_entity.dart';
2121import 'package:analyzer/dart/ast/token.dart';
22+ import 'package:analyzer/dart/constant/value.dart';
2223import 'package:analyzer/dart/element/element.dart';
2324import 'package:analyzer/dart/element/scope.dart';
2425import 'package:analyzer/dart/element/type.dart';
26+ import 'package:analyzer/diagnostic/diagnostic.dart';
27+ import 'package:analyzer/error/listener.dart';
2528import 'package:analyzer/source/line_info.dart';
2629import 'package:analyzer/src/dart/ast/extensions.dart';
2730import 'package:analyzer/src/dart/ast/to_source_visitor.dart';
31+ import 'package:analyzer/src/dart/ast/token.dart';
32+ import 'package:analyzer/src/dart/constant/compute.dart';
33+ import 'package:analyzer/src/dart/constant/evaluation.dart';
34+ import 'package:analyzer/src/dart/constant/utilities.dart';
35+ import 'package:analyzer/src/dart/constant/value.dart';
2836import 'package:analyzer/src/dart/element/element.dart';
2937import 'package:analyzer/src/dart/element/type.dart';
3038import 'package:analyzer/src/dart/element/type_schema.dart';
3139import 'package:analyzer/src/dart/resolver/body_inference_context.dart';
3240import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
41+ import 'package:analyzer/src/error/codes.g.dart';
3342import 'package:analyzer/src/fasta/token_utils.dart' as util show findPrevious;
3443import 'package:analyzer/src/generated/inference_log.dart';
3544import 'package:analyzer/src/generated/resolver.dart';
3645import 'package:analyzer/src/generated/utilities_dart.dart';
46+ import 'package:analyzer/src/lint/constants.dart';
3747import 'package:analyzer/src/utilities/extensions/object.dart';
3848import 'package:collection/collection.dart';
3949import 'package:meta/meta.dart';
@@ -1865,6 +1875,21 @@ abstract class AstVisitor<R> {
18651875 R? visitYieldStatement(YieldStatement node);
18661876}
18671877
1878+ /// The result of attempting to evaluate an expression as a constant.
1879+ @AnalyzerPublicApi(message: 'exported by lib/dart/ast/ast.dart')
1880+ final class AttemptedConstantEvaluationResult {
1881+ /// The value of the expression, or `null` if has [diagnostics].
1882+ ///
1883+ /// If evaluating a constant expression yields diagnostics, then the value of
1884+ /// the constant expression cannot be calculated.
1885+ final DartObject? value;
1886+
1887+ /// The diagnostics reported during the evaluation.
1888+ final List<Diagnostic> diagnostics;
1889+
1890+ AttemptedConstantEvaluationResult._(this.value, this.diagnostics);
1891+ }
1892+
18681893/// The augmented expression.
18691894///
18701895/// It is created only inside an augmentation.
@@ -8007,6 +8032,12 @@ final class ExportDirectiveImpl extends NamespaceDirectiveImpl
80078032/// | [ThrowExpression]
80088033@AnalyzerPublicApi(message: 'exported by lib/dart/ast/ast.dart')
80098034abstract final class Expression implements CollectionElement {
8035+ /// Whether it would be valid for this expression to have a `const` keyword.
8036+ ///
8037+ /// Note that this method can cause constant evaluation to occur, which can be
8038+ /// computationally expensive.
8039+ bool get canBeConst;
8040+
80108041 /// The parameter element representing the parameter to which the value of
80118042 /// this expression is bound.
80128043 ///
@@ -8064,6 +8095,14 @@ abstract final class Expression implements CollectionElement {
80648095 /// unwrapping the expression inside the parentheses. Otherwise, returns this
80658096 /// expression.
80668097 Expression get unParenthesized;
8098+
8099+ /// Computes the constant value of this expression, if it has one.
8100+ ///
8101+ /// Returns a [AttemptedConstantEvaluationResult], containing both the computed
8102+ /// constant value, and a list of errors that occurred during the computation.
8103+ ///
8104+ /// Returns `null` if this expression is not a constant expression.
8105+ AttemptedConstantEvaluationResult? computeConstantValue();
80678106}
80688107
80698108/// A function body consisting of a single expression.
@@ -8215,6 +8254,9 @@ sealed class ExpressionImpl extends AstNodeImpl
82158254 implements CollectionElementImpl, Expression {
82168255 TypeImpl? _staticType;
82178256
8257+ @override
8258+ bool get canBeConst => false;
8259+
82188260 @experimental
82198261 @override
82208262 FormalParameterElementMixin? get correspondingParameter {
@@ -8264,6 +8306,53 @@ sealed class ExpressionImpl extends AstNodeImpl
82648306 @override
82658307 ExpressionImpl get unParenthesized => this;
82668308
8309+ @override
8310+ AttemptedConstantEvaluationResult? computeConstantValue() {
8311+ var unitNode = thisOrAncestorOfType<CompilationUnitImpl>();
8312+ var unitFragment = unitNode?.declaredFragment;
8313+ if (unitFragment == null) {
8314+ throw ArgumentError('This AST structure has not yet been resolved.');
8315+ }
8316+
8317+ var libraryElement = unitFragment.element;
8318+ var declaredVariables = libraryElement.session.declaredVariables;
8319+
8320+ var evaluationEngine = ConstantEvaluationEngine(
8321+ declaredVariables: declaredVariables,
8322+ configuration: ConstantEvaluationConfiguration(),
8323+ );
8324+
8325+ var dependencies = <ConstantEvaluationTarget>[];
8326+ accept(ReferenceFinder(dependencies.add));
8327+
8328+ computeConstants(
8329+ declaredVariables: declaredVariables,
8330+ constants: dependencies,
8331+ featureSet: libraryElement.featureSet,
8332+ configuration: ConstantEvaluationConfiguration(),
8333+ );
8334+
8335+ var errorListener = RecordingErrorListener();
8336+ var visitor = ConstantVisitor(
8337+ evaluationEngine,
8338+ libraryElement,
8339+ ErrorReporter(errorListener, unitFragment.source),
8340+ );
8341+
8342+ var constant = visitor.evaluateAndReportInvalidConstant(this);
8343+ var isInvalidConstant = errorListener.errors.any(
8344+ (e) => e.errorCode == CompileTimeErrorCode.INVALID_CONSTANT,
8345+ );
8346+ if (isInvalidConstant) {
8347+ return null;
8348+ }
8349+
8350+ return AttemptedConstantEvaluationResult._(
8351+ constant is DartObjectImpl ? constant : null,
8352+ errorListener.errors,
8353+ );
8354+ }
8355+
82678356 /// Returns the [AstNode] that puts node into the constant context, and
82688357 /// the explicit `const` keyword of that node. The keyword might be absent
82698358 /// if the constness is implicit.
@@ -14057,6 +14146,25 @@ final class InstanceCreationExpressionImpl extends ExpressionImpl
1405714146 return constructorName.beginToken;
1405814147 }
1405914148
14149+ @override
14150+ bool get canBeConst {
14151+ var element = constructorName.element;
14152+ if (element == null || !element.isConst) return false;
14153+
14154+ // Ensure that dependencies (e.g. default parameter values) are computed.
14155+ element.baseElement.computeConstantDependencies();
14156+
14157+ // Verify that the evaluation of the constructor would not produce an
14158+ // exception.
14159+ var oldKeyword = keyword;
14160+ try {
14161+ keyword = KeywordToken(Keyword.CONST, offset);
14162+ return !hasConstantVerifierError;
14163+ } finally {
14164+ keyword = oldKeyword;
14165+ }
14166+ }
14167+
1406014168 @generated
1406114169 @override
1406214170 ConstructorNameImpl get constructorName => _constructorName;
@@ -24540,6 +24648,17 @@ sealed class TypedLiteralImpl extends LiteralImpl implements TypedLiteral {
2454024648 _becomeParentOf(_typeArguments);
2454124649 }
2454224650
24651+ @override
24652+ bool get canBeConst {
24653+ var oldKeyword = constKeyword;
24654+ try {
24655+ constKeyword = KeywordToken(Keyword.CONST, offset);
24656+ return !hasConstantVerifierError;
24657+ } finally {
24658+ constKeyword = oldKeyword;
24659+ }
24660+ }
24661+
2454324662 @override
2454424663 bool get isConst {
2454524664 return constKeyword != null || inConstantContext;
0 commit comments