Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(856)

Unified Diff: third_party/closure_compiler/error_filter.py

Issue 776273003: Filter out more spurious Promise-related type checking errors. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address feedback. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/closure_compiler/error_filter.py
diff --git a/third_party/closure_compiler/error_filter.py b/third_party/closure_compiler/error_filter.py
index 54cb3a10abc5e8e7e16a8e0dde50b157ad630ed1..a5a549a69b94abd397b6f77cea400f277e2ffea7 100644
--- a/third_party/closure_compiler/error_filter.py
+++ b/third_party/closure_compiler/error_filter.py
@@ -3,73 +3,49 @@
# found in the LICENSE file.
"""Implement filtering out closure compiler errors due to incorrect type-
-checking on promise chains.
+checking on promise-based return types.
-For example, this code would raise a compiler error:
+The compiler's type-checker doesn't correctly unwrap Promised return values
+prior to type-checking them. There are a couple of scenarios where this occurs,
+examples can be found below in the code that deals with each specific scenario.
- Promise.resolve()
- .then(
- /** @return {!Promise<string>} */
- function() { return Promise.resolve('foo'); })
- .then(
- /** @param {string} s */
- function(s) { console.log(s); });
-
-The compiler, while type-checking this code, doesn't unwrap the Promise returned
-by the first callback in the Promise chain. It incorrectly deduces that the
-second function needs to take a {Promise<string>} as an argument, and emits an
-error. This class filters out these types of errors.
+This filtering code applies a set of matchers to the errors that the compiler
+emits. Each matcher fits a known pattern for compiler errors arising from the
+issue described above. If any of the matchers matches an error, that error is
+filtered out of the error list.
Note that this is just a coarse filter. It doesn't, for example, check that the
unwrapped promise type actually matches the type accepted by the next callback
in the Promise chain. Doing so would be the correct way to fix this problem,
but that fix belongs in the compiler.
+
"""
import re
+
class PromiseErrorFilter:
"""Runs checks to filter out promise chain errors."""
def __init__(self):
- # For the code cited above, the compiler would emit an error like:
- # """
- # ERROR - actual parameter 1 of Promise.prototype.then does not match formal
- # parameter
- # found : function (string): undefined
- # required: (function (Promise<string>): ?|null|undefined)
- # function(s) { console.log(s); });
- # ^
- # """
-
- # Matches the initial error message.
- self._error_msg = ("ERROR - actual parameter 1 of Promise.prototype.then "
- "does not match formal parameter")
-
- # Matches the "found:" line.
- # Examples:
- # - function (string): Promise<string>
- # - function ((SomeType|null)): SomeOtherType
- self._found_line_regex = re.compile("found\s*:\s*function\s*\(.*\):\s*.*")
-
- # Matches the "required:" line.
- # Examples:
- # - (function(Promise<string>): ?|null|undefined)
- self._required_line_regex = re.compile(
- "required:\s*\(function\s*\(Promise<.*>\):\s*.*\)")
+ self._allowed_error_patterns = [
+ ChainedPromisePattern(),
+ ReturnedPromisePattern()
+ ]
def filter(self, error_list):
- """Filters promise chain errors out of the given list.
+ """Filters out errors matching any of the allowed patterns.
Args:
error_list: A list of errors from the closure compiler.
Return:
- A list of errors, with promise chain errors removed.
+ A list of errors, with spurious Promise type errors removed.
"""
return [error for error in error_list if not self._should_ignore(error)];
def _should_ignore(self, error):
- """Should the given error be ignored?
+ """Check the given error against all the filters. An error should be
+ ignored if it is a match for any of the allowed message patterns.
Args:
error: A single entry from the closure compiler error list.
@@ -77,12 +53,100 @@ class PromiseErrorFilter:
Return:
True if the error should be ignored, False otherwise.
"""
+ return any([pattern.match(error)
+ for pattern in self._allowed_error_patterns]);
+
+
+class ErrorPattern:
+ """A matcher for compiler error messages. This matches compiler type errors,
+ which look like:
+ # ERROR - <some error message>
+ # found : <some type expression>
+ # required: <some type expression>
+ The message and type expressions are customizable.
+ """
+ def __init__(self, msg, found_pattern, required_pattern):
+ # A string literal that is compared to the first line of the error.
+ self._error_msg = msg
+ # A regex for matching the found type.
+ self._found_line_regex = re.compile("found\s*:\s*" + found_pattern)
+ # A regex for matching the required type.
+ self._required_line_regex = re.compile("required:\s*" + required_pattern)
+
+ def match(self, error):
error_lines = error.split('\n')
+ # Match the error message to see if this pattern applies to the given error.
+ # If the error message matches, then compare the found and required lines.
if self._error_msg not in error_lines[0]:
- # Only ignore promise chain errors.
return False
else:
- # Check that the rest of the error message matches the expected form.
return (self._found_line_regex.match(error_lines[1]) and
self._required_line_regex.match(error_lines[2]))
+
+
+class ChainedPromisePattern(ErrorPattern):
+ """Matcher for spurious errors arising from chained promises. Example code:
+
+ Promise.resolve()
+ .then(
+ /** @return {!Promise<string>} */
+ function() { return Promise.resolve('foo'); })
+ .then(
+ /** @param {string} s */
+ function(s) { console.log(s); });
+
+ The compiler will emit an error that looks like
+
+ ERROR - actual parameter 1 of Promise.prototype.then does not match formal
+ parameter
+ found : function (string): undefined
+ required: (function (Promise<string>): ?|null|undefined)
+ """
+ def __init__(self):
+ # Matches the initial error message.
+ msg = ("ERROR - actual parameter 1 of Promise.prototype.then "
+ "does not match formal parameter")
+
+ # Examples:
+ # - function (string): Promise<string>
+ # - function ((SomeType|null)): SomeOtherType
+ found_pattern = "function\s*\(.*\):\s*.*"
+
+ # Examples:
+ # - (function(Promise<string>): ?|null|undefined)
+ required_pattern = "\(function\s*\(Promise<.*>\):\s*.*\)"
+
+ ErrorPattern.__init__(self, msg, found_pattern, required_pattern)
+
+
+class ReturnedPromisePattern(ErrorPattern):
+ """Matcher for spurious errors arising from Promised return values. Example
+ code:
+
+ /** @return {!Promise<string>} */
+ var getStringAsync = function() {
+ /** @return {!Promise<string>} */
+ var generateString = function() {return Promise.resolve('foo');};
+ return Promise.resolve().then(generateString);
+ };
+
+ The compiler will emit an error that looks like
+
+ ERROR - inconsistent return type
+ found : Promise<Promise<string>>
+ required: Promise<string>
+ """
+ def __init__(self):
+ # Matches the initial error message.
+ msg = "ERROR - inconsistent return type"
+
+ # Example:
+ # - Promise<Promise<string>>
+ found_pattern = "Promise<Promise<[^<>]*>"
+
+ # Example:
+ # - Promise<string>
+ required_pattern = "Promise<[^<>]*>"
+
+ ErrorPattern.__init__(self, msg, found_pattern, required_pattern)
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698