Skip to content

Commit 7818db2

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Add analysis hint for invalid use of @visibleForTemplate code.
Bug: #33353 Change-Id: Iaafccc3dca6b8d87bd54ed721871c72e9ac456c8 Reviewed-on: https://dart-review.googlesource.com/68432 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 71d9601 commit 7818db2

File tree

8 files changed

+305
-7
lines changed

8 files changed

+305
-7
lines changed

pkg/analyzer/lib/dart/element/element.dart

+12
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,12 @@ abstract class Element implements AnalysisTarget {
649649
*/
650650
bool get hasRequired;
651651

652+
/**
653+
* Return `true` if this element has an annotation of the form
654+
* `@visibleForTemplate`.
655+
*/
656+
bool get hasVisibleForTemplate;
657+
652658
/**
653659
* Return `true` if this element has an annotation of the form
654660
* `@visibleForTesting`.
@@ -948,6 +954,12 @@ abstract class ElementAnnotation implements ConstantEvaluationTarget {
948954
*/
949955
bool get isRequired;
950956

957+
/**
958+
* Return `true` if this annotation marks the associated member as being
959+
* visible for template files.
960+
*/
961+
bool get isVisibleForTemplate;
962+
951963
/**
952964
* Return `true` if this annotation marks the associated member as being
953965
* visible for testing.

pkg/analyzer/lib/error/error.dart

+1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ const List<ErrorCode> errorCodeValues = const [
305305
HintCode.INVALID_METHOD_OVERRIDE_TYPE_PARAMETER_BOUND,
306306
HintCode.INVALID_REQUIRED_PARAM,
307307
HintCode.INVALID_USE_OF_PROTECTED_MEMBER,
308+
HintCode.INVALID_USE_OF_VISIBLE_FOR_TEMPLATE_MEMBER,
308309
HintCode.INVALID_USE_OF_VISIBLE_FOR_TESTING_MEMBER,
309310
HintCode.IS_DOUBLE,
310311
HintCode.IS_INT,

pkg/analyzer/lib/src/dart/element/element.dart

+28
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,12 @@ class ElementAnnotationImpl implements ElementAnnotation {
29062906
*/
29072907
static String _MUST_CALL_SUPER_VARIABLE_NAME = "mustCallSuper";
29082908

2909+
/**
2910+
* The name of `angular.meta` library, used to define angular analysis
2911+
* annotations.
2912+
*/
2913+
static String _NG_META_LIB_NAME = "angular.meta";
2914+
29092915
/**
29102916
* The name of the top-level variable used to mark a method as being expected
29112917
* to override an inherited method.
@@ -2935,6 +2941,10 @@ class ElementAnnotationImpl implements ElementAnnotation {
29352941
*/
29362942
static String _REQUIRED_VARIABLE_NAME = "required";
29372943

2944+
/// The name of the top-level variable used to mark a method as being
2945+
/// visible for templates.
2946+
static String _VISIBLE_FOR_TEMPLATE_VARIABLE_NAME = "visibleForTemplate";
2947+
29382948
/// The name of the top-level variable used to mark a method as being
29392949
/// visible for testing.
29402950
static String _VISIBLE_FOR_TESTING_VARIABLE_NAME = "visibleForTesting";
@@ -3065,6 +3075,12 @@ class ElementAnnotationImpl implements ElementAnnotation {
30653075
element.name == _REQUIRED_VARIABLE_NAME &&
30663076
element.library?.name == _META_LIB_NAME;
30673077

3078+
@override
3079+
bool get isVisibleForTemplate =>
3080+
element is PropertyAccessorElement &&
3081+
element.name == _VISIBLE_FOR_TEMPLATE_VARIABLE_NAME &&
3082+
element.library?.name == _NG_META_LIB_NAME;
3083+
30683084
@override
30693085
bool get isVisibleForTesting =>
30703086
element is PropertyAccessorElement &&
@@ -3281,6 +3297,10 @@ abstract class ElementImpl implements Element {
32813297
bool get hasRequired =>
32823298
metadata.any((ElementAnnotation annotation) => annotation.isRequired);
32833299

3300+
@override
3301+
bool get hasVisibleForTemplate => metadata
3302+
.any((ElementAnnotation annotation) => annotation.isVisibleForTemplate);
3303+
32843304
@override
32853305
bool get hasVisibleForTesting => metadata
32863306
.any((ElementAnnotation annotation) => annotation.isVisibleForTesting);
@@ -3382,6 +3402,9 @@ abstract class ElementImpl implements Element {
33823402
setModifier(Modifier.SYNTHETIC, isSynthetic);
33833403
}
33843404

3405+
bool get isVisibleForTemplate => metadata
3406+
.any((ElementAnnotation annotation) => annotation.isVisibleForTemplate);
3407+
33853408
@override
33863409
bool get isVisibleForTesting => metadata
33873410
.any((ElementAnnotation annotation) => annotation.isVisibleForTesting);
@@ -7665,6 +7688,9 @@ class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
76657688
@override
76667689
bool get hasRequired => false;
76677690

7691+
@override
7692+
bool get hasVisibleForTemplate => false;
7693+
76687694
@override
76697695
bool get hasVisibleForTesting => false;
76707696

@@ -7704,6 +7730,8 @@ class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
77047730
@override
77057731
bool get isSynthetic => true;
77067732

7733+
bool get isVisibleForTemplate => false;
7734+
77077735
@override
77087736
bool get isVisibleForTesting => false;
77097737

pkg/analyzer/lib/src/dart/element/handle.dart

+6
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,9 @@ abstract class ElementHandle implements Element {
380380
@override
381381
bool get hasRequired => actualElement.hasRequired;
382382

383+
@override
384+
bool get hasVisibleForTemplate => actualElement.hasVisibleForTemplate;
385+
383386
@override
384387
bool get hasVisibleForTesting => actualElement.hasVisibleForTesting;
385388

@@ -413,6 +416,9 @@ abstract class ElementHandle implements Element {
413416
@override
414417
bool get isSynthetic => actualElement.isSynthetic;
415418

419+
@override
420+
bool get isVisibleForTemplate => actualElement.hasVisibleForTemplate;
421+
416422
@override
417423
bool get isVisibleForTesting => actualElement.hasVisibleForTesting;
418424

pkg/analyzer/lib/src/dart/element/member.dart

+6
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ abstract class Member implements Element {
418418
@override
419419
bool get hasRequired => _baseElement.hasRequired;
420420

421+
@override
422+
bool get hasVisibleForTemplate => _baseElement.hasVisibleForTemplate;
423+
421424
@override
422425
bool get hasVisibleForTesting => _baseElement.hasVisibleForTesting;
423426

@@ -454,6 +457,9 @@ abstract class Member implements Element {
454457
@override
455458
bool get isSynthetic => _baseElement.isSynthetic;
456459

460+
@override
461+
bool get isVisibleForTemplate => _baseElement.hasVisibleForTemplate;
462+
457463
@override
458464
bool get isVisibleForTesting => _baseElement.hasVisibleForTesting;
459465

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

+10
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,16 @@ class HintCode extends ErrorCode {
309309
"The member '{0}' can only be used within instance members of subclasses "
310310
"of '{1}'.");
311311

312+
/// This hint is generated anywhere where a member annotated with
313+
/// `@visibleForTemplate` is used outside of a "template" Dart file.
314+
///
315+
/// Parameters:
316+
/// 0: the name of the member
317+
/// 1: the name of the defining class
318+
static const HintCode INVALID_USE_OF_VISIBLE_FOR_TEMPLATE_MEMBER =
319+
const HintCode('INVALID_USE_OF_VISIBLE_FOR_TEMPLATE_MEMBER',
320+
"The member '{0}' can only be used within '{1}' or a template library.");
321+
312322
/// This hint is generated anywhere where a member annotated with
313323
/// `@visibleForTesting` is used outside the defining library, or a test.
314324
///

pkg/analyzer/lib/src/generated/resolver.dart

+50-5
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
233233

234234
static String _TO_INT_METHOD_NAME = "toInt";
235235

236+
static final _templateExtension = '.template';
237+
236238
static final _testDir = '${path.separator}test${path.separator}';
237239

238240
static final _testingDir = '${path.separator}testing${path.separator}';
@@ -849,9 +851,13 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
849851
/// getter/setter, method closure or invocation accessed outside a subclass,
850852
/// or accessed outside the library wherein the identifier is declared, or
851853
/// * if the given identifier is a closure, field, getter, setter, method
854+
/// closure or invocation which is annotated with `visibleForTemplate`, and
855+
/// is accessed outside of the defining library, and the current library
856+
/// does not have the suffix '.template' in its source path, or
857+
/// * if the given identifier is a closure, field, getter, setter, method
852858
/// closure or invocation which is annotated with `visibleForTesting`, and
853859
/// is accessed outside of the defining library, and the current library
854-
/// does not have the word 'test' in its name.
860+
/// does not have a directory named 'test' or 'testing' in its path.
855861
void _checkForInvalidAccess(SimpleIdentifier identifier) {
856862
if (identifier.inDeclarationContext()) {
857863
return;
@@ -871,6 +877,21 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
871877
return false;
872878
}
873879

880+
bool isVisibleForTemplate(Element element) {
881+
if (element == null) {
882+
return false;
883+
}
884+
if (element.hasVisibleForTemplate) {
885+
return true;
886+
}
887+
if (element is PropertyAccessorElement &&
888+
element.enclosingElement is ClassElement &&
889+
element.variable.hasVisibleForTemplate) {
890+
return true;
891+
}
892+
return false;
893+
}
894+
874895
bool isVisibleForTesting(Element element) {
875896
if (element == null) {
876897
return false;
@@ -897,12 +918,19 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
897918
identifier.parent is Combinator &&
898919
identifier.parent.parent is ExportDirective;
899920

921+
bool inTemplateSource(LibraryElement library) =>
922+
library.definingCompilationUnit.source.fullName
923+
.contains(_templateExtension);
924+
900925
bool inTestDirectory(LibraryElement library) =>
901926
library.definingCompilationUnit.source.fullName.contains(_testDir) ||
902927
library.definingCompilationUnit.source.fullName.contains(_testingDir);
903928

904929
Element element = identifier.staticElement;
905-
if (!isProtected(element) && !isVisibleForTesting(element)) {
930+
if (!isProtected(element) &&
931+
!isVisibleForTemplate(element) &&
932+
!isVisibleForTesting(element)) {
933+
// Without any of these annotations, the access is valid.
906934
return;
907935
}
908936

@@ -920,26 +948,43 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
920948
return;
921949
}
922950
}
951+
if (isVisibleForTemplate(element)) {
952+
if (inCurrentLibrary(element) ||
953+
inTemplateSource(_currentLibrary) ||
954+
inExportDirective(identifier) ||
955+
inCommentReference(identifier)) {
956+
// The access is valid; even if [element] is also marked `protected`,
957+
// the "visibilities" are unioned.
958+
return;
959+
}
960+
}
923961
if (isVisibleForTesting(element)) {
924962
if (inCurrentLibrary(element) ||
925963
inTestDirectory(_currentLibrary) ||
926964
inExportDirective(identifier) ||
927965
inCommentReference(identifier)) {
928-
// The access is valid; even if [element] is also marked
929-
// `protected`, the "visibilities" are unioned.
966+
// The access is valid; even if [element] is also marked `protected`,
967+
// the "visibilities" are unioned.
930968
return;
931969
}
932970
}
933971

934972
// At this point, [identifier] was not cleared as protected access, nor
935-
// cleared as access for testing. Report the appropriate violation(s).
973+
// cleared as access for templates or testing. Report the appropriate
974+
// violation(s).
936975
Element definingClass = element.enclosingElement;
937976
if (isProtected(element)) {
938977
_errorReporter.reportErrorForNode(
939978
HintCode.INVALID_USE_OF_PROTECTED_MEMBER,
940979
identifier,
941980
[identifier.name.toString(), definingClass.name]);
942981
}
982+
if (isVisibleForTemplate(element)) {
983+
_errorReporter.reportErrorForNode(
984+
HintCode.INVALID_USE_OF_VISIBLE_FOR_TEMPLATE_MEMBER,
985+
identifier,
986+
[identifier.name.toString(), definingClass.name]);
987+
}
943988
if (isVisibleForTesting(element)) {
944989
_errorReporter.reportErrorForNode(
945990
HintCode.INVALID_USE_OF_VISIBLE_FOR_TESTING_MEMBER,

0 commit comments

Comments
 (0)