diff --git a/mypy/build.py b/mypy/build.py index 21fde0239c16..32a8ac302255 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1139,6 +1139,20 @@ def compute_hash(text: str) -> str: return hashlib.md5(text.encode('utf-8')).hexdigest() +def compute_interface_hash(tree: MypyFile, manager: BuildManager) -> str: + """Compute interface hash for a tree. + + Should be called when we want to interface hash but don't want to + write the cache. + """ + data = tree.serialize() + if manager.options.debug_cache: + data_str = json.dumps(data, indent=2, sort_keys=True) + else: + data_str = json.dumps(data, sort_keys=True) + return compute_hash(data_str) + + def write_cache(id: str, path: str, tree: MypyFile, dependencies: List[str], suppressed: List[str], child_modules: List[str], dep_prios: List[int], @@ -1947,15 +1961,23 @@ def write_cache(self) -> None: assert self.tree is not None, "Internal error: method must be called on parsed file only" if not self.path or self.options.cache_dir == os.devnull: return + is_errors_for_file = self.manager.errors.is_errors_for_file(self.path) if self.manager.options.quick_and_dirty: - is_errors = self.manager.errors.is_errors_for_file(self.path) + is_errors = is_errors_for_file else: is_errors = self.transitive_error if is_errors: - delete_cache(self.id, self.path, self.manager) - self.meta = None - self.mark_interface_stale(on_errors=True) - return + new_interface_hash = compute_interface_hash(self.tree, self.manager) + if new_interface_hash != self.interface_hash: + delete_cache(self.id, self.path, self.manager) + self.meta = None + self.mark_interface_stale(on_errors=True) + self.interface_hash = new_interface_hash + return + if is_errors_for_file: + delete_cache(self.id, self.path, self.manager) + self.meta = None + return dep_prios = self.dependency_priorities() new_interface_hash, self.meta = write_cache( self.id, self.path, self.tree, diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 6df0d81a686b..b71a8be9d78e 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -201,7 +201,7 @@ def foo() -> int: return "foo" return inner2() -[rechecked mod1, mod2] +[rechecked mod2] [stale] [out2] tmp/mod2.py:4: error: Incompatible return value type (got "str", expected "int")