@@ -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