Improved file filtering for the Static Analysis feature (#4570)

* Improved file filtering for the Static Analysis feature

* Better handling of legacy "check_patterns" option

* Rename "check_src_filter" to plural form to better represent functionality

* Move to plural forms of filter variables
This commit is contained in:
Valerii Koval authored and GitHub committed 2023-03-19 00:45:59 +02:00
1 parent f2d206ca54
commit d9ff250f82
8 files changed
+224 -76

No files matched your search

+22 -6
View File
@@ -49,7 +49,12 @@ from platformio.project.helpers import find_project_dir_above, get_project_dir
exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True
),
)
@click.option("--pattern", multiple=True)
@click.option("--pattern", multiple=True, hidden=True)
@click.option(
"-f",
"--src-filters",
multiple=True
)
@click.option("--flags", multiple=True)
@click.option(
"--severity", multiple=True, type=click.Choice(DefectItem.SEVERITY_LABELS.values())
@@ -67,6 +72,7 @@ def cli(
environment,
project_dir,
project_conf,
src_filters,
pattern,
flags,
severity,
@@ -105,14 +111,24 @@ def cli(
"%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v)
)
default_patterns = [
config.get("platformio", "src_dir"),
config.get("platformio", "include_dir"),
default_src_filters = [
"+<%s>" % os.path.basename(config.get("platformio", "src_dir")),
"+<%s>" % os.path.basename(config.get("platformio", "include_dir")),
]
src_filters = (
src_filters
or pattern
or env_options.get(
"check_src_filters",
env_options.get("check_patterns", default_src_filters),
)
)
tool_options = dict(
verbose=verbose,
silent=silent,
patterns=pattern or env_options.get("check_patterns", default_patterns),
src_filters=src_filters,
flags=flags or env_options.get("check_flags"),
severity=[DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]]
if silent
@@ -265,7 +281,7 @@ def print_defects_stats(results):
tabular_data.append(total)
headers = ["Component"]
headers.extend([l.upper() for l in severity_labels])
headers.extend([label.upper() for label in severity_labels])
headers = [click.style(h, bold=True) for h in headers]
click.echo(tabulate(tabular_data, headers=headers, numalign="center"))
click.echo()
+32 -12
View File
@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import glob
import os
import tempfile
@@ -30,6 +29,7 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
self.config = config
self.envname = envname
self.options = options
self.project_dir = project_dir
self.cc_flags = []
self.cxx_flags = []
self.cpp_includes = []
@@ -41,7 +41,7 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
self._defects = []
self._on_defect_callback = None
self._bad_input = False
self._load_cpp_data(project_dir)
self._load_cpp_data()
# detect all defects by default
if not self.options.get("severity"):
@@ -56,8 +56,8 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
for s in self.options["severity"]
]
def _load_cpp_data(self, project_dir):
data = load_build_metadata(project_dir, self.envname)
def _load_cpp_data(self):
data = load_build_metadata(self.project_dir, self.envname)
if not data:
return
self.cc_flags = click.parser.split_arg_string(data.get("cc_flags", ""))
@@ -99,6 +99,13 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
includes_file,
)
result = proc.exec_command(cmd, shell=True)
if result["returncode"] != 0:
click.echo("Warning: Failed to extract toolchain defines!")
if self.options.get("verbose"):
click.echo(result["out"])
click.echo(result["err"])
for line in result["out"].split("\n"):
tokens = line.strip().split(" ", 2)
if not tokens or tokens[0] != "#define":
@@ -201,7 +208,7 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
return result
@staticmethod
def get_project_target_files(patterns):
def get_project_target_files(project_dir, src_filters):
c_extension = (".c",)
cpp_extensions = (".cc", ".cpp", ".cxx", ".ino")
header_extensions = (".h", ".hh", ".hpp", ".hxx")
@@ -216,13 +223,9 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
elif path.endswith(cpp_extensions):
result["c++"].append(os.path.abspath(path))
for pattern in patterns:
for item in glob.glob(pattern, recursive=True):
if not os.path.isdir(item):
_add_file(item)
for root, _, files in os.walk(item, followlinks=True):
for f in files:
_add_file(os.path.join(root, f))
src_filters = normalize_src_filters(src_filters)
for f in fs.match_src_files(project_dir, src_filters):
_add_file(f)
return result
@@ -243,3 +246,20 @@ class CheckToolBase: # pylint: disable=too-many-instance-attributes
self.clean_up()
return self._bad_input
#
# Helpers
#
def normalize_src_filters(src_filters):
def _normalize(src_filters):
return (
src_filters if src_filters.startswith(("+<", "-<")) else "+<%s>" % src_filters
)
if isinstance(src_filters, (list, tuple)):
return " ".join([_normalize(f) for f in src_filters])
return _normalize(src_filters)
+3 -1
View File
@@ -64,7 +64,9 @@ class ClangtidyCheckTool(CheckToolBase):
):
cmd.append("--checks=*")
project_files = self.get_project_target_files(self.options["patterns"])
project_files = self.get_project_target_files(
self.project_dir, self.options["src_filters"]
)
src_files = []
for items in project_files.values():
+7 -3
View File
@@ -96,7 +96,7 @@ class CppcheckCheckTool(CheckToolBase):
)
click.echo()
self._bad_input = True
self._buffer = ""
self._buffer = ""
return None
self._buffer = ""
@@ -214,7 +214,9 @@ class CppcheckCheckTool(CheckToolBase):
if not self.is_flag_set("--addon", self.get_flags("cppcheck")):
return
for files in self.get_project_target_files(self.options["patterns"]).values():
for files in self.get_project_target_files(
self.project_dir, self.options["src_filters"]
).values():
for f in files:
dump_file = f + ".dump"
if os.path.isfile(dump_file):
@@ -243,7 +245,9 @@ class CppcheckCheckTool(CheckToolBase):
def check(self, on_defect_callback=None):
self._on_defect_callback = on_defect_callback
project_files = self.get_project_target_files(self.options["patterns"])
project_files = self.get_project_target_files(
self.project_dir, self.options["src_filters"]
)
src_files_scope = ("c", "c++")
if not any(project_files[t] for t in src_files_scope):
click.echo("Error: Nothing to check.")
+1 -1
View File
@@ -227,7 +227,7 @@ class PvsStudioCheckTool(CheckToolBase): # pylint: disable=too-many-instance-at
def check(self, on_defect_callback=None):
self._on_defect_callback = on_defect_callback
for scope, files in self.get_project_target_files(
self.options["patterns"]
self.project_dir, self.options["src_filters"]
).items():
if scope not in ("c", "c++"):
continue
+2 -1
View File
@@ -649,7 +649,8 @@ ProjectOptions = OrderedDict(
),
ConfigEnvOption(
group="check",
name="check_patterns",
name="check_src_filters",
oldnames=["check_patterns"],
description=(
"Configure a list of target files or directories for checking "
"(Unix shell-style wildcards)"