blob: 92b5d6e369c839261471d77da1b91568c5355610 [file] [log] [blame]
// Copyright (c) 2016, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
import 'package:analyzer/src/generated/testing/element_factory.dart';
import 'package:analyzer/src/generated/testing/test_type_provider.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/test_support.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstantFinderTest);
defineReflectiveTests(ReferenceFinderTest);
});
}
@reflectiveTest
class ConstantFinderTest {
late final AstNode _node;
late final TypeProvider _typeProvider;
late final Source _source;
void setUp() {
_typeProvider = TestTypeProvider();
_source = TestSource();
}
/// Test an annotation that consists solely of an identifier (and hence
/// represents a reference to a compile-time constant variable).
void test_visitAnnotation_constantVariable() {
var compilationUnitElement =
ElementFactory.compilationUnit('/test.dart', _source)..source = _source;
ElementFactory.library(_AnalysisContextMock(), 'L')
.definingCompilationUnit = compilationUnitElement;
ElementAnnotationImpl elementAnnotation =
ElementAnnotationImpl(compilationUnitElement);
var annotation = AstTestFactory.annotation(AstTestFactory.identifier3('x'));
_node = elementAnnotation.annotationAst = annotation
..elementAnnotation = elementAnnotation;
expect(_findAnnotations(), contains(_node));
}
void test_visitAnnotation_enumConstant() {
// Analyzer ignores annotations on enum constant declarations.
Annotation annotation = AstTestFactory.annotation2(
AstTestFactory.identifier3('A'), null, AstTestFactory.argumentList());
_node = astFactory.enumConstantDeclaration(
null, <Annotation>[annotation], AstTestFactory.identifier3('C'));
expect(_findConstants(), isEmpty);
}
/// Test an annotation that represents the invocation of a constant
/// constructor.
void test_visitAnnotation_invocation() {
var compilationUnitElement =
ElementFactory.compilationUnit('/test.dart', _source)..source = _source;
ElementFactory.library(_AnalysisContextMock(), 'L')
.definingCompilationUnit = compilationUnitElement;
ElementAnnotationImpl elementAnnotation =
ElementAnnotationImpl(compilationUnitElement);
var annotation = AstTestFactory.annotation2(
AstTestFactory.identifier3('A'), null, AstTestFactory.argumentList());
_node = elementAnnotation.annotationAst = annotation
..elementAnnotation = elementAnnotation;
expect(_findAnnotations(), contains(_node));
}
void test_visitAnnotation_partOf() {
// Analyzer ignores annotations on "part of" directives.
Annotation annotation = AstTestFactory.annotation2(
AstTestFactory.identifier3('A'), null, AstTestFactory.argumentList());
_node = AstTestFactory.partOfDirective2(<Annotation>[annotation],
AstTestFactory.libraryIdentifier2(<String>['L']));
expect(_findConstants(), isEmpty);
}
void test_visitConstructorDeclaration_const() {
ConstructorElement element = _setupConstructorDeclaration("A", true);
expect(_findConstants(), contains(element));
}
void test_visitConstructorDeclaration_nonConst() {
_setupConstructorDeclaration("A", false);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_const() {
VariableElement element = _setupVariableDeclaration("v", true, true);
expect(_findConstants(), contains(element));
}
void test_visitVariableDeclaration_final_inClass() {
_setupFieldDeclaration('C', 'f', Keyword.FINAL);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_final_inClassWithConstConstructor() {
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.FINAL,
hasConstConstructor: true);
expect(_findConstants(), contains(field.declaredElement));
}
void test_visitVariableDeclaration_final_outsideClass() {
_setupVariableDeclaration('v', false, true, isFinal: true);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_noInitializer() {
_setupVariableDeclaration("v", true, false);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_nonConst() {
_setupVariableDeclaration("v", false, true);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_static_const_inClass() {
VariableDeclaration field =
_setupFieldDeclaration('C', 'f', Keyword.CONST, isStatic: true);
expect(_findConstants(), contains(field.declaredElement));
}
void
test_visitVariableDeclaration_static_const_inClassWithConstConstructor() {
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.CONST,
isStatic: true, hasConstConstructor: true);
expect(_findConstants(), contains(field.declaredElement));
}
void
test_visitVariableDeclaration_static_final_inClassWithConstConstructor() {
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.FINAL,
isStatic: true, hasConstConstructor: true);
expect(_findConstants(), isNot(contains(field.declaredElement)));
}
void
test_visitVariableDeclaration_uninitialized_final_inClassWithConstConstructor() {
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.FINAL,
isInitialized: false, hasConstConstructor: true);
expect(_findConstants(), isNot(contains(field.declaredElement)));
}
void test_visitVariableDeclaration_uninitialized_static_const_inClass() {
_setupFieldDeclaration('C', 'f', Keyword.CONST,
isStatic: true, isInitialized: false);
expect(_findConstants(), isEmpty);
}
List<Annotation> _findAnnotations() {
Set<Annotation> annotations = <Annotation>{};
for (ConstantEvaluationTarget target in _findConstants()) {
if (target is ElementAnnotationImpl) {
expect(target.source, same(_source));
annotations.add(target.annotationAst);
}
}
return List<Annotation>.from(annotations);
}
List<ConstantEvaluationTarget> _findConstants() {
ConstantFinder finder = ConstantFinder();
_node.accept(finder);
List<ConstantEvaluationTarget> constants = finder.constantsToCompute;
expect(constants, isNotNull);
return constants;
}
ConstructorElement _setupConstructorDeclaration(String name, bool isConst) {
var constKeyword = isConst ? Keyword.CONST : null;
var constructorDeclaration = AstTestFactory.constructorDeclaration2(
constKeyword,
null,
AstTestFactory.identifier3(name),
null,
AstTestFactory.formalParameterList(),
[],
AstTestFactory.blockFunctionBody2());
ClassElement classElement = ElementFactory.classElement2(name);
ConstructorElement element =
ElementFactory.constructorElement(classElement, name, isConst);
constructorDeclaration.declaredElement = element;
_node = constructorDeclaration;
return element;
}
VariableDeclaration _setupFieldDeclaration(
String className, String fieldName, Keyword keyword,
{bool isInitialized = true,
bool isStatic = false,
bool hasConstConstructor = false}) {
var variableDeclaration = isInitialized
? AstTestFactory.variableDeclaration2(
fieldName, AstTestFactory.integer(0))
: AstTestFactory.variableDeclaration(fieldName);
var fieldElement = ElementFactory.fieldElement(
fieldName,
isStatic,
keyword == Keyword.FINAL,
keyword == Keyword.CONST,
_typeProvider.intType);
variableDeclaration.name.staticElement = fieldElement;
FieldDeclaration fieldDeclaration = AstTestFactory.fieldDeclaration2(
isStatic, keyword, <VariableDeclaration>[variableDeclaration]);
var classDeclaration = AstTestFactory.classDeclaration(
null, className, null, null, null, null);
classDeclaration.members.add(fieldDeclaration);
_node = classDeclaration;
ClassElementImpl classElement = ElementFactory.classElement2(className);
classElement.fields = <FieldElement>[fieldElement];
classDeclaration.name.staticElement = classElement;
if (hasConstConstructor) {
var constructorDeclaration = AstTestFactory.constructorDeclaration2(
Keyword.CONST,
null,
AstTestFactory.identifier3(className),
null,
AstTestFactory.formalParameterList(),
[],
AstTestFactory.blockFunctionBody2());
classDeclaration.members.add(constructorDeclaration);
ConstructorElement constructorElement =
ElementFactory.constructorElement(classElement, '', true);
constructorDeclaration.declaredElement = constructorElement;
classElement.constructors = <ConstructorElement>[constructorElement];
} else {
classElement.constructors = const <ConstructorElement>[];
}
return variableDeclaration;
}
VariableElement _setupVariableDeclaration(
String name, bool isConst, bool isInitialized,
{isFinal = false}) {
var variableDeclaration = isInitialized
? AstTestFactory.variableDeclaration2(name, AstTestFactory.integer(0))
: AstTestFactory.variableDeclaration(name);
var identifier = variableDeclaration.name;
VariableElement element = ElementFactory.localVariableElement(identifier);
identifier.staticElement = element;
var keyword = isConst
? Keyword.CONST
: isFinal
? Keyword.FINAL
: null;
AstTestFactory.variableDeclarationList2(keyword, [variableDeclaration]);
_node = variableDeclaration;
return element;
}
}
@reflectiveTest
class ReferenceFinderTest {
late final Element _tail;
final List<ConstantEvaluationTarget> _dependencies = [];
void test_visitSimpleIdentifier_const() {
_visitNode(_makeTailVariable("v2", true));
_assertOneArc(_tail);
}
void test_visitSuperConstructorInvocation_const() {
_visitNode(_makeTailSuperConstructorInvocation("A", true));
_assertOneArc(_tail);
}
void test_visitSuperConstructorInvocation_nonConst() {
_visitNode(_makeTailSuperConstructorInvocation("A", false));
_assertOneArc(_tail);
}
void test_visitSuperConstructorInvocation_unresolved() {
SuperConstructorInvocation superConstructorInvocation =
AstTestFactory.superConstructorInvocation();
_visitNode(superConstructorInvocation);
_assertNoArcs();
}
void _assertNoArcs() {
expect(_dependencies, isEmpty);
}
void _assertOneArc(Element tail) {
expect(_dependencies, hasLength(1));
expect(_dependencies[0], same(tail));
}
SuperConstructorInvocation _makeTailSuperConstructorInvocation(
String name, bool isConst) {
List<ConstructorInitializer> initializers = <ConstructorInitializer>[];
var constructorDeclaration = AstTestFactory.constructorDeclaration(
AstTestFactory.identifier3(name),
null,
AstTestFactory.formalParameterList(),
initializers);
if (isConst) {
constructorDeclaration.constKeyword = KeywordToken(Keyword.CONST, 0);
}
ClassElementImpl classElement = ElementFactory.classElement2(name);
var superConstructorInvocation =
AstTestFactory.superConstructorInvocation();
ConstructorElementImpl constructorElement =
ElementFactory.constructorElement(classElement, name, isConst);
_tail = constructorElement;
superConstructorInvocation.staticElement = constructorElement;
return superConstructorInvocation;
}
SimpleIdentifier _makeTailVariable(String name, bool isConst) {
VariableDeclaration variableDeclaration =
AstTestFactory.variableDeclaration(name);
ConstLocalVariableElementImpl variableElement =
ElementFactory.constLocalVariableElement(name);
_tail = variableElement;
variableElement.isConst = isConst;
AstTestFactory.variableDeclarationList2(
isConst ? Keyword.CONST : Keyword.VAR, [variableDeclaration]);
var identifier = AstTestFactory.identifier3(name);
identifier.staticElement = variableElement;
return identifier;
}
void _visitNode(AstNode node) {
var referenceFinder = ReferenceFinder((dependency) {
_dependencies.add(dependency);
});
node.accept(referenceFinder);
}
}
class _AnalysisContextMock implements AnalysisContext {
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}