Source code for statick_tool.plugins.tool.uncrustify

"""Apply uncrustify tool and gather results."""

import argparse
import difflib
import logging
import subprocess
from typing import Optional

from statick_tool.issue import Issue
from statick_tool.package import Package
from statick_tool.tool_plugin import ToolPlugin


[docs] class UncrustifyToolPlugin(ToolPlugin): """Apply uncrustify tool and gather results."""
[docs] def get_name(self) -> str: """Get name of tool. Returns: Name of the tool. """ return "uncrustify"
[docs] def gather_args(self, args: argparse.Namespace) -> None: """Gather arguments. Args: args: Flags for this plugin will be added to these existing arguments. """ args.add_argument( "--uncrustify-bin", dest="uncrustify_bin", type=str, help="uncrustify binary path", )
[docs] def get_binary( # pylint: disable=unused-argument self, level: Optional[str] = None, package: Optional[Package] = None ) -> str: """Get tool binary name. Args: level: The level to process (optional). package: The package to process (optional). Returns: Name of the tool binary. """ binary = self.get_name() if ( self.plugin_context is not None and self.plugin_context.args.uncrustify_bin is not None ): binary = self.plugin_context.args.uncrustify_bin return binary
[docs] def scan( # pylint: disable=too-many-locals, too-many-branches self, package: Package, level: str ) -> Optional[list[Issue]]: """Run tool and gather output. Args: package: The package to process. level: The level to process. Returns: List of issues or None. """ if "make_targets" not in package and "headers" not in package: return [] if self.plugin_context is None: return None uncrustify_bin = self.get_binary() flags: list[str] = [] flags += self.get_user_flags(level) files: list[str] = [] if "make_targets" in package: for target in package["make_targets"]: files += target["src"] if "headers" in package: files += package["headers"] total_output: list[str] = [] try: format_file_name = self.plugin_context.resources.get_file("uncrustify.cfg") for src in files: cmd = [uncrustify_bin, "-c", format_file_name, "-f", src] output = subprocess.check_output( cmd, # type: ignore stderr=subprocess.STDOUT, universal_newlines=True, ) src_cmd = ["cat", src] src_output = subprocess.check_output( src_cmd, stderr=subprocess.STDOUT, universal_newlines=True ) diff = difflib.context_diff( output.splitlines(), src_output.splitlines() ) found_diff = False output = output.split("\n", 1)[1] for line in diff: if ( line.startswith("---") or line.startswith("***") or line.startswith("! Parsing") or src in line or line.isspace() ): continue # This is a bug I can't figure out yet. if "#ifndef" in line or "#define" in line: continue found_diff = True if found_diff: total_output.append(src) except subprocess.CalledProcessError as ex: output = ex.output logging.warning("uncrustify failed! Returncode = %d", ex.returncode) logging.warning("%s exception: %s", self.get_name(), ex.output) return None except OSError as ex: logging.warning("Couldn't find uncrustify executable! (%s)", ex) return None for output in total_output: logging.debug("%s", output) if self.plugin_context and self.plugin_context.args.output_directory: with open(self.get_name() + ".log", "w", encoding="utf8") as fid: for output in total_output: fid.write(output) issues: list[Issue] = self.parse_output(total_output, package) return issues
[docs] def parse_output( self, total_output: list[str], package: Optional[Package] = None ) -> list[Issue]: """Parse tool output and report issues. Args: total_output: List of output strings. package: The package to process (optional). Returns: List of issues. """ issues: list[Issue] = [] for output in total_output: issues.append( Issue( output, 0, self.get_name(), "format", 1, "Uncrustify mis-match", None, ) ) return issues