Here we take input generators and return a dict with issues,
        preconditions, and postconditions separated. If there is only a single
        generator file, it's simple. If we also pass in a generator from a
        previous inputfile then there are a couple extra steps:

        1. If an issue was seen in the previous inputfile then we won't return
        it, because it's not new.
        2. In addition, we take an optional linemap file that maps for each
        filename, each new file line position to a list of old file line
        position. This is used to adjust handles to we can recognize when issues
        moved.

def analysis_output_to_dict_entries(
        self,
        inputfile: AnalysisOutput,
        previous_inputfile: Optional[AnalysisOutput],
        previous_issue_handles: Optional[AnalysisOutput],
        linemapfile: Optional[str],
    ) -> DictEntries:
        """Here we take input generators and return a dict with issues,
        preconditions, and postconditions separated. If there is only a single
        generator file, it's simple. If we also pass in a generator from a
        previous inputfile then there are a couple extra steps:

        1. If an issue was seen in the previous inputfile then we won't return
        it, because it's not new.
        2. In addition, we take an optional linemap file that maps for each
        filename, each new file line position to a list of old file line
        position. This is used to adjust handles to we can recognize when issues
        moved.
        """

        issues = []
        previous_handles: Set[str] = set()
        # pyre-fixme[9]: conditions has type `Dict[ParseType, Dict[str, List[Dict[str...
        conditions: Dict[ParseType, Dict[str, List[Dict[str, Any]]]] = {
            ParseType.PRECONDITION: defaultdict(list),
            ParseType.POSTCONDITION: defaultdict(list),
        }

        # If we have a mapfile, create the map.
        if linemapfile:
            log.info("Parsing linemap file")
            with open(linemapfile, "r") as f:
                linemap = json.load(f)
        else:
            linemap = None

        # Save entry info from the parent analysis, if there is one.
        # If previous issue handles file is provided, use it over
        # previous_inputfile (contains the full JSON)
        if previous_issue_handles:
            log.info("Parsing previous issue handles")
            for f in previous_issue_handles.file_handles():
                handles = f.read().splitlines()
                previous_handles = {handle for handle in handles}
        elif previous_inputfile:
            log.info("Parsing previous hh_server output")
            for typ, master_key, e in self._analysis_output_to_parsed_types(
                previous_inputfile
            ):
                if typ == ParseType.ISSUE:
                    diff_handle = BaseParser.compute_diff_handle(
                        e["filename"], e["line"], e["code"]
                    )
                    previous_handles.add(diff_handle)
                    # Use exact handle match too in case linemap is missing.
                    previous_handles.add(master_key)

        log.info("Parsing hh_server output")
        for typ, key, e in self._analysis_output_to_parsed_types(inputfile):
            if typ == ParseType.ISSUE:
                # We are only interested in issues that weren't in the previous
                # analysis.
                if not self._is_existing_issue(linemap, previous_handles, e, key):
                    issues.append(e)
            else:
                conditions[typ][key].append(e)

        return {
            "issues": issues,
            "preconditions": conditions[ParseType.PRECONDITION],
            "postconditions": conditions[ParseType.POSTCONDITION],
        }