@@ -206,6 +206,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
206
206
@override
207
207
Object visitMethodInvocation (MethodInvocation node) {
208
208
_checkForCanBeNullAfterNullAware (node.realTarget, node.operator );
209
+ _checkForInvalidProtectedMethodCalls (node);
209
210
return super .visitMethodInvocation (node);
210
211
}
211
212
@@ -607,6 +608,39 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
607
608
return false ;
608
609
}
609
610
611
+ /**
612
+ * Produces a hint if the given invocation is of a protected method outside
613
+ * a subclass instance method.
614
+ */
615
+ void _checkForInvalidProtectedMethodCalls (MethodInvocation node) {
616
+ Element element = node.methodName.bestElement;
617
+ if (element == null || ! element.isProtected) {
618
+ return ;
619
+ }
620
+
621
+ ClassElement definingClass = element.enclosingElement;
622
+
623
+ MethodDeclaration decl =
624
+ node.getAncestor ((AstNode node) => node is MethodDeclaration );
625
+ if (decl == null ) {
626
+ _errorReporter.reportErrorForNode (
627
+ HintCode .INVALID_USE_OF_PROTECTED_MEMBER ,
628
+ node,
629
+ [node.methodName.toString (), definingClass.name]);
630
+ return ;
631
+ }
632
+
633
+ ClassElement invokingClass = decl.element? .enclosingElement;
634
+ if (invokingClass != null ) {
635
+ if (! _hasSuperClassOrMixin (invokingClass, definingClass.type)) {
636
+ _errorReporter.reportErrorForNode (
637
+ HintCode .INVALID_USE_OF_PROTECTED_MEMBER ,
638
+ node,
639
+ [node.methodName.toString (), definingClass.name]);
640
+ }
641
+ }
642
+ }
643
+
610
644
/**
611
645
* Check that the imported library does not define a loadLibrary function. The import has already
612
646
* been determined to be deferred when this is called.
@@ -789,35 +823,6 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
789
823
return false ;
790
824
}
791
825
792
- /**
793
- * Check for the passed class declaration for the
794
- * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code.
795
- *
796
- * @param node the class declaration to check
797
- * @return `true` if and only if a hint code is generated on the passed node
798
- * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] .
799
- */
800
- // bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) {
801
- // ClassElement classElement = node.element;
802
- // if (classElement == null) {
803
- // return false;
804
- // }
805
- // MethodElement equalsOperatorMethodElement =
806
- // classElement.getMethod(sc.TokenType.EQ_EQ.lexeme);
807
- // if (equalsOperatorMethodElement != null) {
808
- // PropertyAccessorElement hashCodeElement =
809
- // classElement.getGetter(_HASHCODE_GETTER_NAME);
810
- // if (hashCodeElement == null) {
811
- // _errorReporter.reportErrorForNode(
812
- // HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE,
813
- // node.name,
814
- // [classElement.displayName]);
815
- // return true;
816
- // }
817
- // }
818
- // return false;
819
- // }
820
-
821
826
/**
822
827
* Generate a hint for `noSuchMethod` methods that do nothing except of
823
828
* calling another `noSuchMethod` that is not defined by `Object` .
@@ -866,6 +871,35 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
866
871
return false ;
867
872
}
868
873
874
+ /**
875
+ * Check for the passed class declaration for the
876
+ * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code.
877
+ *
878
+ * @param node the class declaration to check
879
+ * @return `true` if and only if a hint code is generated on the passed node
880
+ * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] .
881
+ */
882
+ // bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) {
883
+ // ClassElement classElement = node.element;
884
+ // if (classElement == null) {
885
+ // return false;
886
+ // }
887
+ // MethodElement equalsOperatorMethodElement =
888
+ // classElement.getMethod(sc.TokenType.EQ_EQ.lexeme);
889
+ // if (equalsOperatorMethodElement != null) {
890
+ // PropertyAccessorElement hashCodeElement =
891
+ // classElement.getGetter(_HASHCODE_GETTER_NAME);
892
+ // if (hashCodeElement == null) {
893
+ // _errorReporter.reportErrorForNode(
894
+ // HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE,
895
+ // node.name,
896
+ // [classElement.displayName]);
897
+ // return true;
898
+ // }
899
+ // }
900
+ // return false;
901
+ // }
902
+
869
903
/**
870
904
* Check for situations where the result of a method or function is used, when it returns 'void'.
871
905
*
@@ -891,6 +925,24 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
891
925
return false ;
892
926
}
893
927
928
+ bool _hasSuperClassOrMixin (ClassElement element, InterfaceType type) {
929
+ List <ClassElement > seenClasses = < ClassElement > [];
930
+ while (element != null && ! seenClasses.contains (element)) {
931
+ if (element.type == type) {
932
+ return true ;
933
+ }
934
+
935
+ if (element.mixins.any ((InterfaceType t) => t == type)) {
936
+ return true ;
937
+ }
938
+
939
+ seenClasses.add (element);
940
+ element = element.supertype? .element;
941
+ }
942
+
943
+ return false ;
944
+ }
945
+
894
946
/**
895
947
* Given a parenthesized expression, this returns the parent (or recursively grand-parent) of the
896
948
* expression that is a parenthesized expression, but whose parent is not a parenthesized
@@ -8383,26 +8435,6 @@ class ResolverVisitor extends ScopedVisitor {
8383
8435
return null ;
8384
8436
}
8385
8437
8386
- void _inferArgumentTypesFromContext (InvocationExpression node) {
8387
- DartType contextType = node.staticInvokeType;
8388
- if (contextType is FunctionType ) {
8389
- DartType originalType = node.function.staticType;
8390
- DartType returnContextType = InferenceContext .getType (node);
8391
- TypeSystem ts = typeSystem;
8392
- if (returnContextType != null &&
8393
- node.typeArguments == null &&
8394
- originalType is FunctionType &&
8395
- originalType.typeFormals.isNotEmpty &&
8396
- ts is StrongTypeSystemImpl ) {
8397
-
8398
- contextType = ts.inferGenericFunctionCall (typeProvider, originalType,
8399
- DartType .EMPTY_LIST , DartType .EMPTY_LIST , returnContextType);
8400
- }
8401
-
8402
- InferenceContext .setType (node.argumentList, contextType);
8403
- }
8404
- }
8405
-
8406
8438
@override
8407
8439
Object visitNamedExpression (NamedExpression node) {
8408
8440
InferenceContext .setType (node.expression, InferenceContext .getType (node));
@@ -8730,6 +8762,25 @@ class ResolverVisitor extends ScopedVisitor {
8730
8762
return null ;
8731
8763
}
8732
8764
8765
+ void _inferArgumentTypesFromContext (InvocationExpression node) {
8766
+ DartType contextType = node.staticInvokeType;
8767
+ if (contextType is FunctionType ) {
8768
+ DartType originalType = node.function.staticType;
8769
+ DartType returnContextType = InferenceContext .getType (node);
8770
+ TypeSystem ts = typeSystem;
8771
+ if (returnContextType != null &&
8772
+ node.typeArguments == null &&
8773
+ originalType is FunctionType &&
8774
+ originalType.typeFormals.isNotEmpty &&
8775
+ ts is StrongTypeSystemImpl ) {
8776
+ contextType = ts.inferGenericFunctionCall (typeProvider, originalType,
8777
+ DartType .EMPTY_LIST , DartType .EMPTY_LIST , returnContextType);
8778
+ }
8779
+
8780
+ InferenceContext .setType (node.argumentList, contextType);
8781
+ }
8782
+ }
8783
+
8733
8784
void _inferFormalParameterList (FormalParameterList node, DartType type) {
8734
8785
if (typeAnalyzer.inferFormalParameterList (node, type)) {
8735
8786
// TODO(leafp): This gets dropped on the floor if we're in the field
0 commit comments