Chromium Code Reviews| Index: pkg/analyzer/lib/src/generated/resolver.dart |
| diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart |
| index 59359c19e033baa505730e881a6fa2a963e29a0b..805765cc07bb608614d05f7fe77cd7992747dbb5 100644 |
| --- a/pkg/analyzer/lib/src/generated/resolver.dart |
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart |
| @@ -206,6 +206,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| @override |
| Object visitMethodInvocation(MethodInvocation node) { |
| _checkForCanBeNullAfterNullAware(node.realTarget, node.operator); |
| + _checkForInvalidProtectedMethodCalls(node); |
| return super.visitMethodInvocation(node); |
| } |
| @@ -608,6 +609,39 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| } |
| /** |
| + * Produces a hint if the given invocation is of a protected method outside |
| + * a subclass instance method. |
| + */ |
| + void _checkForInvalidProtectedMethodCalls(MethodInvocation node) { |
| + Element element = node.methodName.bestElement; |
| + if (element == null || !element.isProtected) { |
| + return; |
| + } |
| + |
| + ClassElement definingClass = element.enclosingElement; |
| + |
| + MethodDeclaration decl = |
| + node.getAncestor((AstNode node) => node is MethodDeclaration); |
| + if (decl == null) { |
|
Brian Wilkerson
2016/02/23 23:42:57
Are we handling static methods as a call site?
cl
pquitslund
2016/02/24 17:12:17
Yep. Test added!
|
| + _errorReporter.reportErrorForNode( |
| + HintCode.INVALID_USE_OF_PROTECTED_MEMBER, |
| + node, |
| + [node.methodName.toString(), definingClass.name]); |
| + return; |
| + } |
| + |
| + ClassElement invokingClass = decl.element?.enclosingElement; |
| + if (invokingClass != null) { |
| + if (!_hasSuperClassOrMixin(invokingClass, definingClass.type)) { |
| + _errorReporter.reportErrorForNode( |
| + HintCode.INVALID_USE_OF_PROTECTED_MEMBER, |
| + node, |
| + [node.methodName.toString(), definingClass.name]); |
| + } |
| + } |
| + } |
| + |
| + /** |
| * Check that the imported library does not define a loadLibrary function. The import has already |
| * been determined to be deferred when this is called. |
| * |
| @@ -790,35 +824,6 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| } |
| /** |
| - * Check for the passed class declaration for the |
| - * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code. |
| - * |
| - * @param node the class declaration to check |
| - * @return `true` if and only if a hint code is generated on the passed node |
| - * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE]. |
| - */ |
| -// bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) { |
| -// ClassElement classElement = node.element; |
| -// if (classElement == null) { |
| -// return false; |
| -// } |
| -// MethodElement equalsOperatorMethodElement = |
| -// classElement.getMethod(sc.TokenType.EQ_EQ.lexeme); |
| -// if (equalsOperatorMethodElement != null) { |
| -// PropertyAccessorElement hashCodeElement = |
| -// classElement.getGetter(_HASHCODE_GETTER_NAME); |
| -// if (hashCodeElement == null) { |
| -// _errorReporter.reportErrorForNode( |
| -// HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE, |
| -// node.name, |
| -// [classElement.displayName]); |
| -// return true; |
| -// } |
| -// } |
| -// return false; |
| -// } |
| - |
| - /** |
| * Generate a hint for `noSuchMethod` methods that do nothing except of |
| * calling another `noSuchMethod` that is not defined by `Object`. |
| * |
| @@ -867,6 +872,35 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| } |
| /** |
| + * Check for the passed class declaration for the |
| + * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code. |
| + * |
| + * @param node the class declaration to check |
| + * @return `true` if and only if a hint code is generated on the passed node |
| + * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE]. |
| + */ |
| +// bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) { |
| +// ClassElement classElement = node.element; |
| +// if (classElement == null) { |
| +// return false; |
| +// } |
| +// MethodElement equalsOperatorMethodElement = |
| +// classElement.getMethod(sc.TokenType.EQ_EQ.lexeme); |
| +// if (equalsOperatorMethodElement != null) { |
| +// PropertyAccessorElement hashCodeElement = |
| +// classElement.getGetter(_HASHCODE_GETTER_NAME); |
| +// if (hashCodeElement == null) { |
| +// _errorReporter.reportErrorForNode( |
| +// HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE, |
| +// node.name, |
| +// [classElement.displayName]); |
| +// return true; |
| +// } |
| +// } |
| +// return false; |
| +// } |
| + |
| + /** |
| * Check for situations where the result of a method or function is used, when it returns 'void'. |
| * |
| * TODO(jwren) Many other situations of use could be covered. We currently cover the cases var x = |
| @@ -891,6 +925,24 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| return false; |
| } |
| + bool _hasSuperClassOrMixin(ClassElement element, InterfaceType type) { |
| + List<ClassElement> seenClasses = <ClassElement>[]; |
| + while (element != null && !seenClasses.contains(element)) { |
| + if (element.type == type) { |
| + return true; |
| + } |
| + |
| + if (element.mixins.any((InterfaceType t) => t == type)) { |
| + return true; |
| + } |
| + |
| + seenClasses.add(element); |
| + element = element.supertype?.element; |
| + } |
| + |
| + return false; |
| + } |
| + |
| /** |
| * Given a parenthesized expression, this returns the parent (or recursively grand-parent) of the |
| * expression that is a parenthesized expression, but whose parent is not a parenthesized |
| @@ -8387,26 +8439,6 @@ class ResolverVisitor extends ScopedVisitor { |
| return null; |
| } |
| - void _inferArgumentTypesFromContext(InvocationExpression node) { |
| - DartType contextType = node.staticInvokeType; |
| - if (contextType is FunctionType) { |
| - DartType originalType = node.function.staticType; |
| - DartType returnContextType = InferenceContext.getType(node); |
| - TypeSystem ts = typeSystem; |
| - if (returnContextType != null && |
| - node.typeArguments == null && |
| - originalType is FunctionType && |
| - originalType.typeFormals.isNotEmpty && |
| - ts is StrongTypeSystemImpl) { |
| - |
| - contextType = ts.inferGenericFunctionCall(typeProvider, originalType, |
| - DartType.EMPTY_LIST, DartType.EMPTY_LIST, returnContextType); |
| - } |
| - |
| - InferenceContext.setType(node.argumentList, contextType); |
| - } |
| - } |
| - |
| @override |
| Object visitNamedExpression(NamedExpression node) { |
| InferenceContext.setType(node.expression, InferenceContext.getType(node)); |
| @@ -8734,6 +8766,25 @@ class ResolverVisitor extends ScopedVisitor { |
| return null; |
| } |
| + void _inferArgumentTypesFromContext(InvocationExpression node) { |
| + DartType contextType = node.staticInvokeType; |
| + if (contextType is FunctionType) { |
| + DartType originalType = node.function.staticType; |
| + DartType returnContextType = InferenceContext.getType(node); |
| + TypeSystem ts = typeSystem; |
| + if (returnContextType != null && |
| + node.typeArguments == null && |
| + originalType is FunctionType && |
| + originalType.typeFormals.isNotEmpty && |
| + ts is StrongTypeSystemImpl) { |
| + contextType = ts.inferGenericFunctionCall(typeProvider, originalType, |
| + DartType.EMPTY_LIST, DartType.EMPTY_LIST, returnContextType); |
| + } |
| + |
| + InferenceContext.setType(node.argumentList, contextType); |
| + } |
| + } |
| + |
| void _inferFormalParameterList(FormalParameterList node, DartType type) { |
| if (typeAnalyzer.inferFormalParameterList(node, type)) { |
| // TODO(leafp): This gets dropped on the floor if we're in the field |