mirror of
https://github.com/rizsotto/scan-build.git
synced 2025-12-21 12:19:29 +01:00
add more typing
This commit is contained in:
@@ -34,6 +34,10 @@ from libscanbuild.compilation import Compilation, classify_source, \
|
|||||||
CompilationDatabase
|
CompilationDatabase
|
||||||
from libscanbuild.clang import get_version, get_arguments
|
from libscanbuild.clang import get_version, get_arguments
|
||||||
|
|
||||||
|
from typing import Any, Dict, List, Callable, Iterable, Generator # noqa: ignore=F401
|
||||||
|
from libscanbuild import Execution # noqa: ignore=F401
|
||||||
|
import argparse # noqa: ignore=F401
|
||||||
|
|
||||||
__all__ = ['scan_build', 'analyze_build', 'analyze_compiler_wrapper']
|
__all__ = ['scan_build', 'analyze_build', 'analyze_compiler_wrapper']
|
||||||
|
|
||||||
COMPILER_WRAPPER_CC = 'analyze-cc'
|
COMPILER_WRAPPER_CC = 'analyze-cc'
|
||||||
@@ -43,6 +47,7 @@ ENVIRONMENT_KEY = 'ANALYZE_BUILD'
|
|||||||
|
|
||||||
@command_entry_point
|
@command_entry_point
|
||||||
def scan_build():
|
def scan_build():
|
||||||
|
# type: () -> int
|
||||||
""" Entry point for scan-build command. """
|
""" Entry point for scan-build command. """
|
||||||
|
|
||||||
args = parse_args_for_scan_build()
|
args = parse_args_for_scan_build()
|
||||||
@@ -70,6 +75,7 @@ def scan_build():
|
|||||||
|
|
||||||
@command_entry_point
|
@command_entry_point
|
||||||
def analyze_build():
|
def analyze_build():
|
||||||
|
# type: () -> int
|
||||||
""" Entry point for analyze-build command. """
|
""" Entry point for analyze-build command. """
|
||||||
|
|
||||||
args = parse_args_for_analyze_build()
|
args = parse_args_for_analyze_build()
|
||||||
@@ -85,6 +91,7 @@ def analyze_build():
|
|||||||
|
|
||||||
|
|
||||||
def need_analyzer(args):
|
def need_analyzer(args):
|
||||||
|
# type: (str) -> bool
|
||||||
""" Check the intent of the build command.
|
""" Check the intent of the build command.
|
||||||
|
|
||||||
When static analyzer run against project configure step, it should be
|
When static analyzer run against project configure step, it should be
|
||||||
@@ -94,16 +101,18 @@ def need_analyzer(args):
|
|||||||
when compiler wrappers are used. That's the moment when build setup
|
when compiler wrappers are used. That's the moment when build setup
|
||||||
check the compiler and capture the location for the build process. """
|
check the compiler and capture the location for the build process. """
|
||||||
|
|
||||||
return len(args) and not re.search('configure|autogen', args[0])
|
return len(args) > 0 and not re.search('configure|autogen', args[0])
|
||||||
|
|
||||||
|
|
||||||
def analyze_parameters(args):
|
def analyze_parameters(args):
|
||||||
|
# type: (argparse.Namespace) -> Dict[str, Any]
|
||||||
""" Mapping between the command line parameters and the analyzer run
|
""" Mapping between the command line parameters and the analyzer run
|
||||||
method. The run method works with a plain dictionary, while the command
|
method. The run method works with a plain dictionary, while the command
|
||||||
line parameters are in a named tuple.
|
line parameters are in a named tuple.
|
||||||
The keys are very similar, and some values are preprocessed. """
|
The keys are very similar, and some values are preprocessed. """
|
||||||
|
|
||||||
def prefix_with(constant, pieces):
|
def prefix_with(constant, pieces):
|
||||||
|
# type: (Any, List[Any]) -> List[Any]
|
||||||
""" From a sequence create another sequence where every second element
|
""" From a sequence create another sequence where every second element
|
||||||
is from the original sequence and the odd elements are the prefix.
|
is from the original sequence and the odd elements are the prefix.
|
||||||
|
|
||||||
@@ -112,6 +121,7 @@ def analyze_parameters(args):
|
|||||||
return [elem for piece in pieces for elem in [constant, piece]]
|
return [elem for piece in pieces for elem in [constant, piece]]
|
||||||
|
|
||||||
def direct_args(args):
|
def direct_args(args):
|
||||||
|
# type: (argparse.Namespace) -> List[str]
|
||||||
""" A group of command line arguments can mapped to command
|
""" A group of command line arguments can mapped to command
|
||||||
line arguments of the analyzer. """
|
line arguments of the analyzer. """
|
||||||
|
|
||||||
@@ -161,6 +171,7 @@ def analyze_parameters(args):
|
|||||||
|
|
||||||
|
|
||||||
def run_analyzer_parallel(compilations, args):
|
def run_analyzer_parallel(compilations, args):
|
||||||
|
# type: (Iterable[Compilation], argparse.Namespace) -> None
|
||||||
""" Runs the analyzer against the given compilations. """
|
""" Runs the analyzer against the given compilations. """
|
||||||
|
|
||||||
logging.debug('run analyzer against compilation database')
|
logging.debug('run analyzer against compilation database')
|
||||||
@@ -176,6 +187,7 @@ def run_analyzer_parallel(compilations, args):
|
|||||||
|
|
||||||
|
|
||||||
def setup_environment(args):
|
def setup_environment(args):
|
||||||
|
# type: (argparse.Namespace) -> Dict[str, str]
|
||||||
""" Set up environment for build command to interpose compiler wrapper. """
|
""" Set up environment for build command to interpose compiler wrapper. """
|
||||||
|
|
||||||
environment = dict(os.environ)
|
environment = dict(os.environ)
|
||||||
@@ -199,6 +211,7 @@ def setup_environment(args):
|
|||||||
@command_entry_point
|
@command_entry_point
|
||||||
@wrapper_entry_point
|
@wrapper_entry_point
|
||||||
def analyze_compiler_wrapper(result, execution):
|
def analyze_compiler_wrapper(result, execution):
|
||||||
|
# type: (int, Execution) -> None
|
||||||
""" Entry point for `analyze-cc` and `analyze-c++` compiler wrappers. """
|
""" Entry point for `analyze-cc` and `analyze-c++` compiler wrappers. """
|
||||||
|
|
||||||
# don't run analyzer when compilation fails. or when it's not requested.
|
# don't run analyzer when compilation fails. or when it's not requested.
|
||||||
@@ -215,6 +228,7 @@ def analyze_compiler_wrapper(result, execution):
|
|||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def report_directory(hint, keep):
|
def report_directory(hint, keep):
|
||||||
|
# type: (str, bool) -> Generator[str, None, None]
|
||||||
""" Responsible for the report directory.
|
""" Responsible for the report directory.
|
||||||
|
|
||||||
hint -- could specify the parent directory of the output directory.
|
hint -- could specify the parent directory of the output directory.
|
||||||
@@ -278,6 +292,7 @@ def require(required):
|
|||||||
# 'text' or 'plist-multi-file'
|
# 'text' or 'plist-multi-file'
|
||||||
'output_failures']) # generate crash reports or not
|
'output_failures']) # generate crash reports or not
|
||||||
def run(opts):
|
def run(opts):
|
||||||
|
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||||
""" Entry point to run (or not) static analyzer against a single entry
|
""" Entry point to run (or not) static analyzer against a single entry
|
||||||
of the compilation database.
|
of the compilation database.
|
||||||
|
|
||||||
@@ -296,6 +311,7 @@ def run(opts):
|
|||||||
|
|
||||||
|
|
||||||
def logging_analyzer_output(opts):
|
def logging_analyzer_output(opts):
|
||||||
|
# type: (Dict[str, Any]) -> None
|
||||||
""" Display error message from analyzer. """
|
""" Display error message from analyzer. """
|
||||||
|
|
||||||
if opts and 'error_output' in opts:
|
if opts and 'error_output' in opts:
|
||||||
@@ -306,6 +322,7 @@ def logging_analyzer_output(opts):
|
|||||||
@require(['clang', 'directory', 'flags', 'source', 'output_dir', 'language',
|
@require(['clang', 'directory', 'flags', 'source', 'output_dir', 'language',
|
||||||
'error_output', 'exit_code'])
|
'error_output', 'exit_code'])
|
||||||
def report_failure(opts):
|
def report_failure(opts):
|
||||||
|
# type: (Dict[str, Any]) -> None
|
||||||
""" Create report when analyzer failed.
|
""" Create report when analyzer failed.
|
||||||
|
|
||||||
The major report is the preprocessor output. The output filename generated
|
The major report is the preprocessor output. The output filename generated
|
||||||
@@ -313,12 +330,14 @@ def report_failure(opts):
|
|||||||
And some more execution context also saved into '.info.txt' file. """
|
And some more execution context also saved into '.info.txt' file. """
|
||||||
|
|
||||||
def extension():
|
def extension():
|
||||||
|
# type: () -> str
|
||||||
""" Generate preprocessor file extension. """
|
""" Generate preprocessor file extension. """
|
||||||
|
|
||||||
mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
|
mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
|
||||||
return mapping.get(opts['language'], '.i')
|
return mapping.get(opts['language'], '.i')
|
||||||
|
|
||||||
def destination():
|
def destination():
|
||||||
|
# type: () -> str
|
||||||
""" Creates failures directory if not exits yet. """
|
""" Creates failures directory if not exits yet. """
|
||||||
|
|
||||||
failures_dir = os.path.join(opts['output_dir'], 'failures')
|
failures_dir = os.path.join(opts['output_dir'], 'failures')
|
||||||
@@ -332,10 +351,10 @@ def report_failure(opts):
|
|||||||
error = 'crash' if opts['exit_code'] < 0 else 'other_error'
|
error = 'crash' if opts['exit_code'] < 0 else 'other_error'
|
||||||
# Create preprocessor output file name. (This is blindly following the
|
# Create preprocessor output file name. (This is blindly following the
|
||||||
# Perl implementation.)
|
# Perl implementation.)
|
||||||
(handle, name) = tempfile.mkstemp(suffix=extension(),
|
(fd, name) = tempfile.mkstemp(suffix=extension(),
|
||||||
prefix='clang_' + error + '_',
|
prefix='clang_' + error + '_',
|
||||||
dir=destination())
|
dir=destination())
|
||||||
os.close(handle)
|
os.close(fd)
|
||||||
# Execute Clang again, but run the syntax check only.
|
# Execute Clang again, but run the syntax check only.
|
||||||
try:
|
try:
|
||||||
cwd = opts['directory']
|
cwd = opts['directory']
|
||||||
@@ -362,6 +381,7 @@ def report_failure(opts):
|
|||||||
@require(['clang', 'directory', 'flags', 'direct_args', 'source', 'output_dir',
|
@require(['clang', 'directory', 'flags', 'direct_args', 'source', 'output_dir',
|
||||||
'output_format'])
|
'output_format'])
|
||||||
def run_analyzer(opts, continuation=report_failure):
|
def run_analyzer(opts, continuation=report_failure):
|
||||||
|
# type: (...) -> Dict[str, Any]
|
||||||
""" It assembles the analysis command line and executes it. Capture the
|
""" It assembles the analysis command line and executes it. Capture the
|
||||||
output of the analysis and returns with it. If failure reports are
|
output of the analysis and returns with it. If failure reports are
|
||||||
requested, it calls the continuation to generate it. """
|
requested, it calls the continuation to generate it. """
|
||||||
@@ -399,6 +419,7 @@ def run_analyzer(opts, continuation=report_failure):
|
|||||||
|
|
||||||
@require(['flags', 'force_debug'])
|
@require(['flags', 'force_debug'])
|
||||||
def filter_debug_flags(opts, continuation=run_analyzer):
|
def filter_debug_flags(opts, continuation=run_analyzer):
|
||||||
|
# type: (...) -> Dict[str, Any]
|
||||||
""" Filter out nondebug macros when requested. """
|
""" Filter out nondebug macros when requested. """
|
||||||
|
|
||||||
if opts.pop('force_debug'):
|
if opts.pop('force_debug'):
|
||||||
@@ -410,6 +431,7 @@ def filter_debug_flags(opts, continuation=run_analyzer):
|
|||||||
|
|
||||||
@require(['language', 'compiler', 'source', 'flags'])
|
@require(['language', 'compiler', 'source', 'flags'])
|
||||||
def language_check(opts, continuation=filter_debug_flags):
|
def language_check(opts, continuation=filter_debug_flags):
|
||||||
|
# type: (...) -> Dict[str, Any]
|
||||||
""" Find out the language from command line parameters or file name
|
""" Find out the language from command line parameters or file name
|
||||||
extension. The decision also influenced by the compiler invocation. """
|
extension. The decision also influenced by the compiler invocation. """
|
||||||
|
|
||||||
@@ -427,10 +449,10 @@ def language_check(opts, continuation=filter_debug_flags):
|
|||||||
|
|
||||||
if language is None:
|
if language is None:
|
||||||
logging.debug('skip analysis, language not known')
|
logging.debug('skip analysis, language not known')
|
||||||
return None
|
return dict()
|
||||||
elif language not in accepted:
|
elif language not in accepted:
|
||||||
logging.debug('skip analysis, language not supported')
|
logging.debug('skip analysis, language not supported')
|
||||||
return None
|
return dict()
|
||||||
|
|
||||||
logging.debug('analysis, language: %s', language)
|
logging.debug('analysis, language: %s', language)
|
||||||
opts.update({'language': language,
|
opts.update({'language': language,
|
||||||
@@ -440,6 +462,7 @@ def language_check(opts, continuation=filter_debug_flags):
|
|||||||
|
|
||||||
@require(['arch_list', 'flags'])
|
@require(['arch_list', 'flags'])
|
||||||
def arch_check(opts, continuation=language_check):
|
def arch_check(opts, continuation=language_check):
|
||||||
|
# type: (...) -> Dict[str, Any]
|
||||||
""" Do run analyzer through one of the given architectures. """
|
""" Do run analyzer through one of the given architectures. """
|
||||||
|
|
||||||
disabled = frozenset({'ppc', 'ppc64'})
|
disabled = frozenset({'ppc', 'ppc64'})
|
||||||
@@ -459,7 +482,7 @@ def arch_check(opts, continuation=language_check):
|
|||||||
opts.update({'flags': ['-arch', current] + opts['flags']})
|
opts.update({'flags': ['-arch', current] + opts['flags']})
|
||||||
return continuation(opts)
|
return continuation(opts)
|
||||||
logging.debug('skip analysis, found not supported arch')
|
logging.debug('skip analysis, found not supported arch')
|
||||||
return None
|
return dict()
|
||||||
logging.debug('analysis, on default arch')
|
logging.debug('analysis, on default arch')
|
||||||
return continuation(opts)
|
return continuation(opts)
|
||||||
|
|
||||||
@@ -487,11 +510,12 @@ IGNORED_FLAGS = {
|
|||||||
'-sectorder': 3,
|
'-sectorder': 3,
|
||||||
'--param': 1,
|
'--param': 1,
|
||||||
'--serialize-diagnostics': 1
|
'--serialize-diagnostics': 1
|
||||||
}
|
} # type: Dict[str, int]
|
||||||
|
|
||||||
|
|
||||||
@require(['flags'])
|
@require(['flags'])
|
||||||
def classify_parameters(opts, continuation=arch_check):
|
def classify_parameters(opts, continuation=arch_check):
|
||||||
|
# type: (...) -> Dict[str, Any]
|
||||||
""" Prepare compiler flags (filters some and add others) and take out
|
""" Prepare compiler flags (filters some and add others) and take out
|
||||||
language (-x) and architecture (-arch) flags for future processing. """
|
language (-x) and architecture (-arch) flags for future processing. """
|
||||||
|
|
||||||
@@ -500,7 +524,7 @@ def classify_parameters(opts, continuation=arch_check):
|
|||||||
'flags': [], # the filtered compiler flags
|
'flags': [], # the filtered compiler flags
|
||||||
'arch_list': [], # list of architecture flags
|
'arch_list': [], # list of architecture flags
|
||||||
'language': None, # compilation language, None, if not specified
|
'language': None, # compilation language, None, if not specified
|
||||||
}
|
} # type: Dict[str, Any]
|
||||||
|
|
||||||
# iterate on the compile options
|
# iterate on the compile options
|
||||||
args = iter(opts['flags'])
|
args = iter(opts['flags'])
|
||||||
@@ -530,18 +554,20 @@ def classify_parameters(opts, continuation=arch_check):
|
|||||||
|
|
||||||
@require(['source', 'excludes'])
|
@require(['source', 'excludes'])
|
||||||
def exclude(opts, continuation=classify_parameters):
|
def exclude(opts, continuation=classify_parameters):
|
||||||
|
# type: (...) -> Dict[str, Any]
|
||||||
""" Analysis might be skipped, when one of the requested excluded
|
""" Analysis might be skipped, when one of the requested excluded
|
||||||
directory contains the file. """
|
directory contains the file. """
|
||||||
|
|
||||||
def contains(directory, entry):
|
def contains(directory, entry):
|
||||||
|
# type: (str, str) -> bool
|
||||||
""" Check is directory contains the given file. """
|
""" Check is directory contains the given file. """
|
||||||
|
|
||||||
# When a directory contains a file, then the relative path to the
|
# When a directory contains a file, then the relative path to the
|
||||||
# file from that directory does not start with a parent dir prefix.
|
# file from that directory does not start with a parent dir prefix.
|
||||||
relative = os.path.relpath(entry, directory).split(os.sep)
|
relative = os.path.relpath(entry, directory).split(os.sep)
|
||||||
return len(relative) and relative[0] != os.pardir
|
return len(relative) > 0 and relative[0] != os.pardir
|
||||||
|
|
||||||
if any(contains(dir, opts['source']) for dir in opts['excludes']):
|
if any(contains(dir, opts['source']) for dir in opts['excludes']):
|
||||||
logging.debug('skip analysis, file requested to exclude')
|
logging.debug('skip analysis, file requested to exclude')
|
||||||
return None
|
return dict()
|
||||||
return continuation(opts)
|
return continuation(opts)
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ import sys
|
|||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Tuple, Dict # noqa: ignore=F401
|
|
||||||
from libscanbuild import reconfigure_logging
|
from libscanbuild import reconfigure_logging
|
||||||
from libscanbuild.clang import get_checkers
|
from libscanbuild.clang import get_checkers
|
||||||
|
|
||||||
|
from typing import Tuple, Dict # noqa: ignore=F401
|
||||||
|
|
||||||
__all__ = ['parse_args_for_intercept_build', 'parse_args_for_analyze_build',
|
__all__ = ['parse_args_for_intercept_build', 'parse_args_for_analyze_build',
|
||||||
'parse_args_for_scan_build']
|
'parse_args_for_scan_build']
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ Since Clang command line interface is so rich, but this project is using only
|
|||||||
a subset of that, it makes sense to create a function specific wrapper. """
|
a subset of that, it makes sense to create a function specific wrapper. """
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from libscanbuild import shell_split, run_command
|
||||||
|
|
||||||
from typing import List, Set, FrozenSet, Callable # noqa: ignore=F401
|
from typing import List, Set, FrozenSet, Callable # noqa: ignore=F401
|
||||||
from typing import Iterable, Tuple, Dict # noqa: ignore=F401
|
from typing import Iterable, Tuple, Dict # noqa: ignore=F401
|
||||||
from libscanbuild import shell_split, run_command
|
|
||||||
|
|
||||||
__all__ = ['get_version', 'get_arguments', 'get_checkers']
|
__all__ = ['get_version', 'get_arguments', 'get_checkers']
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import os
|
|||||||
import collections
|
import collections
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from typing import List, Iterable, Dict, Tuple, Type, Any # noqa: ignore=F401
|
|
||||||
from libscanbuild import Execution, shell_split, run_command
|
from libscanbuild import Execution, shell_split, run_command
|
||||||
|
|
||||||
|
from typing import List, Iterable, Dict, Tuple, Type, Any # noqa: ignore=F401
|
||||||
|
|
||||||
__all__ = ['classify_source', 'Compilation', 'CompilationDatabase']
|
__all__ = ['classify_source', 'Compilation', 'CompilationDatabase']
|
||||||
|
|
||||||
# Map of ignored compiler option for the creation of a compilation database.
|
# Map of ignored compiler option for the creation of a compilation database.
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
import subprocess
|
import subprocess
|
||||||
import argparse # noqa: ignore=F401
|
|
||||||
from typing import Iterable, Dict, Tuple, List # noqa: ignore=F401
|
|
||||||
|
|
||||||
from libear import build_libear, temporary_directory
|
from libear import build_libear, temporary_directory
|
||||||
from libscanbuild import command_entry_point, wrapper_entry_point, \
|
from libscanbuild import command_entry_point, wrapper_entry_point, \
|
||||||
@@ -38,6 +36,9 @@ from libscanbuild import command_entry_point, wrapper_entry_point, \
|
|||||||
from libscanbuild.arguments import parse_args_for_intercept_build
|
from libscanbuild.arguments import parse_args_for_intercept_build
|
||||||
from libscanbuild.compilation import Compilation, CompilationDatabase
|
from libscanbuild.compilation import Compilation, CompilationDatabase
|
||||||
|
|
||||||
|
from typing import Iterable, Dict, Tuple, List # noqa: ignore=F401
|
||||||
|
import argparse # noqa: ignore=F401
|
||||||
|
|
||||||
__all__ = ['capture', 'intercept_build', 'intercept_compiler_wrapper']
|
__all__ = ['capture', 'intercept_build', 'intercept_compiler_wrapper']
|
||||||
|
|
||||||
COMPILER_WRAPPER_CC = 'intercept-cc'
|
COMPILER_WRAPPER_CC = 'intercept-cc'
|
||||||
|
|||||||
@@ -19,12 +19,19 @@ import glob
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
import getpass
|
||||||
|
import socket
|
||||||
|
|
||||||
from libscanbuild.clang import get_version
|
from libscanbuild.clang import get_version
|
||||||
|
|
||||||
|
from typing import Dict, List, Callable, Any, Set, Generator, Iterator # noqa: ignore=F401
|
||||||
|
import argparse # noqa: ignore=F401
|
||||||
|
|
||||||
__all__ = ['document']
|
__all__ = ['document']
|
||||||
|
|
||||||
|
|
||||||
def document(args):
|
def document(args):
|
||||||
|
# type: (argparse.Namespace) -> int
|
||||||
""" Generates cover report and returns the number of bugs/crashes. """
|
""" Generates cover report and returns the number of bugs/crashes. """
|
||||||
|
|
||||||
html_reports_available = args.output_format in {'html', 'plist-html'}
|
html_reports_available = args.output_format in {'html', 'plist-html'}
|
||||||
@@ -62,11 +69,9 @@ def document(args):
|
|||||||
|
|
||||||
|
|
||||||
def assemble_cover(args, prefix, fragments):
|
def assemble_cover(args, prefix, fragments):
|
||||||
|
# type: (argparse.Namespace, str, List[str]) -> None
|
||||||
""" Put together the fragments into a final report. """
|
""" Put together the fragments into a final report. """
|
||||||
|
|
||||||
import getpass
|
|
||||||
import socket
|
|
||||||
|
|
||||||
if args.html_title is None:
|
if args.html_title is None:
|
||||||
args.html_title = os.path.basename(prefix) + ' - analyzer results'
|
args.html_title = os.path.basename(prefix) + ' - analyzer results'
|
||||||
|
|
||||||
@@ -161,6 +166,7 @@ def bug_summary(output_dir, bug_counter):
|
|||||||
|
|
||||||
|
|
||||||
def bug_report(output_dir, prefix):
|
def bug_report(output_dir, prefix):
|
||||||
|
# type: (str, str) -> str
|
||||||
""" Creates a fragment from the analyzer reports. """
|
""" Creates a fragment from the analyzer reports. """
|
||||||
|
|
||||||
pretty = prettify_bug(prefix, output_dir)
|
pretty = prettify_bug(prefix, output_dir)
|
||||||
@@ -208,6 +214,7 @@ def bug_report(output_dir, prefix):
|
|||||||
|
|
||||||
|
|
||||||
def crash_report(output_dir, prefix):
|
def crash_report(output_dir, prefix):
|
||||||
|
# type: (str, str) -> str
|
||||||
""" Creates a fragment from the compiler crashes. """
|
""" Creates a fragment from the compiler crashes. """
|
||||||
|
|
||||||
pretty = prettify_crash(prefix, output_dir)
|
pretty = prettify_crash(prefix, output_dir)
|
||||||
@@ -246,14 +253,15 @@ def crash_report(output_dir, prefix):
|
|||||||
|
|
||||||
|
|
||||||
def read_crashes(output_dir):
|
def read_crashes(output_dir):
|
||||||
|
# type: (str) -> Iterator[Dict[str, Any]]
|
||||||
""" Generate a unique sequence of crashes from given output directory. """
|
""" Generate a unique sequence of crashes from given output directory. """
|
||||||
|
|
||||||
return (parse_crash(filename)
|
pattern = os.path.join(output_dir, 'failures', '*.info.txt') # type: str
|
||||||
for filename in glob.iglob(os.path.join(output_dir, 'failures',
|
return (parse_crash(filename) for filename in glob.iglob(pattern))
|
||||||
'*.info.txt')))
|
|
||||||
|
|
||||||
|
|
||||||
def read_bugs(output_dir, html):
|
def read_bugs(output_dir, html):
|
||||||
|
# type: (str, bool) -> Iterator[Dict[str, Any]]
|
||||||
""" Generate a unique sequence of bugs from given output directory.
|
""" Generate a unique sequence of bugs from given output directory.
|
||||||
|
|
||||||
Duplicates can be in a project if the same module was compiled multiple
|
Duplicates can be in a project if the same module was compiled multiple
|
||||||
@@ -261,7 +269,7 @@ def read_bugs(output_dir, html):
|
|||||||
the final report (cover) only once. """
|
the final report (cover) only once. """
|
||||||
|
|
||||||
parser = parse_bug_html if html else parse_bug_plist
|
parser = parse_bug_html if html else parse_bug_plist
|
||||||
pattern = '*.html' if html else '*.plist'
|
pattern = '*.html' if html else '*.plist' # type: str
|
||||||
|
|
||||||
duplicate = duplicate_check(
|
duplicate = duplicate_check(
|
||||||
lambda bug: '{bug_line}.{bug_path_length}:{bug_file}'.format(**bug))
|
lambda bug: '{bug_line}.{bug_path_length}:{bug_file}'.format(**bug))
|
||||||
@@ -275,6 +283,7 @@ def read_bugs(output_dir, html):
|
|||||||
|
|
||||||
|
|
||||||
def parse_bug_plist(filename):
|
def parse_bug_plist(filename):
|
||||||
|
# type: (str) -> Generator[Dict[str, Any], None, None]
|
||||||
""" Returns the generator of bugs from a single .plist file. """
|
""" Returns the generator of bugs from a single .plist file. """
|
||||||
|
|
||||||
content = plistlib.readPlist(filename)
|
content = plistlib.readPlist(filename)
|
||||||
@@ -295,6 +304,7 @@ def parse_bug_plist(filename):
|
|||||||
|
|
||||||
|
|
||||||
def parse_bug_html(filename):
|
def parse_bug_html(filename):
|
||||||
|
# type: (str) -> Generator[Dict[str, Any], None, None]
|
||||||
""" Parse out the bug information from HTML output. """
|
""" Parse out the bug information from HTML output. """
|
||||||
|
|
||||||
patterns = [re.compile(r'<!-- BUGTYPE (?P<bug_type>.*) -->$'),
|
patterns = [re.compile(r'<!-- BUGTYPE (?P<bug_type>.*) -->$'),
|
||||||
@@ -333,6 +343,7 @@ def parse_bug_html(filename):
|
|||||||
|
|
||||||
|
|
||||||
def parse_crash(filename):
|
def parse_crash(filename):
|
||||||
|
# type: (str) -> Dict[str, Any]
|
||||||
""" Parse out the crash information from the report file. """
|
""" Parse out the crash information from the report file. """
|
||||||
|
|
||||||
match = re.match(r'(.*)\.info\.txt', filename)
|
match = re.match(r'(.*)\.info\.txt', filename)
|
||||||
@@ -350,11 +361,13 @@ def parse_crash(filename):
|
|||||||
|
|
||||||
|
|
||||||
def category_type_name(bug):
|
def category_type_name(bug):
|
||||||
|
# type: (Dict[str, Any]) -> str
|
||||||
""" Create a new bug attribute from bug by category and type.
|
""" Create a new bug attribute from bug by category and type.
|
||||||
|
|
||||||
The result will be used as CSS class selector in the final report. """
|
The result will be used as CSS class selector in the final report. """
|
||||||
|
|
||||||
def smash(key):
|
def smash(key):
|
||||||
|
# type: (str) -> str
|
||||||
""" Make value ready to be HTML attribute value. """
|
""" Make value ready to be HTML attribute value. """
|
||||||
|
|
||||||
return bug.get(key, '').lower().replace(' ', '_').replace("'", '')
|
return bug.get(key, '').lower().replace(' ', '_').replace("'", '')
|
||||||
@@ -363,6 +376,7 @@ def category_type_name(bug):
|
|||||||
|
|
||||||
|
|
||||||
def duplicate_check(hash_function):
|
def duplicate_check(hash_function):
|
||||||
|
# type: (Callable[[Any], str]) -> Callable[[Dict[str, Any]], bool]
|
||||||
""" Workaround to detect duplicate dictionary values.
|
""" Workaround to detect duplicate dictionary values.
|
||||||
|
|
||||||
Python `dict` type has no `hash` method, which is required by the `set`
|
Python `dict` type has no `hash` method, which is required by the `set`
|
||||||
@@ -375,23 +389,25 @@ def duplicate_check(hash_function):
|
|||||||
This method is a factory method, which returns a predicate. """
|
This method is a factory method, which returns a predicate. """
|
||||||
|
|
||||||
def predicate(entry):
|
def predicate(entry):
|
||||||
|
# type: (Dict[str, Any]) -> bool
|
||||||
""" The predicate which calculates and stores the hash of the given
|
""" The predicate which calculates and stores the hash of the given
|
||||||
entries. The entry type has to work with the given hash function.
|
entries. The entry type has to work with the given hash function.
|
||||||
|
|
||||||
:param entry: the questioned entry,
|
:param entry: the questioned entry,
|
||||||
:return: true/false depends the hash value is already seen or not.
|
:return: true/false depends the hash value is already seen or not.
|
||||||
"""
|
"""
|
||||||
entry_hash = hash_function(entry)
|
entry_hash = hash_function(entry) # type: str
|
||||||
if entry_hash not in state:
|
if entry_hash not in state:
|
||||||
state.add(entry_hash)
|
state.add(entry_hash)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
state = set()
|
state = set() # type: Set[str]
|
||||||
return predicate
|
return predicate
|
||||||
|
|
||||||
|
|
||||||
def create_counters():
|
def create_counters():
|
||||||
|
# type () -> Callable[[Dict[str, Any]], None] FIXME
|
||||||
""" Create counters for bug statistics.
|
""" Create counters for bug statistics.
|
||||||
|
|
||||||
Two entries are maintained: 'total' is an integer, represents the
|
Two entries are maintained: 'total' is an integer, represents the
|
||||||
@@ -401,6 +417,7 @@ def create_counters():
|
|||||||
and 'label'. """
|
and 'label'. """
|
||||||
|
|
||||||
def predicate(bug):
|
def predicate(bug):
|
||||||
|
# type (Dict[str, Any]) -> None FIXME
|
||||||
bug_category = bug['bug_category']
|
bug_category = bug['bug_category']
|
||||||
bug_type = bug['bug_type']
|
bug_type = bug['bug_type']
|
||||||
current_category = predicate.categories.get(bug_category, dict())
|
current_category = predicate.categories.get(bug_category, dict())
|
||||||
@@ -414,13 +431,15 @@ def create_counters():
|
|||||||
predicate.categories.update({bug_category: current_category})
|
predicate.categories.update({bug_category: current_category})
|
||||||
predicate.total += 1
|
predicate.total += 1
|
||||||
|
|
||||||
predicate.total = 0
|
predicate.total = 0 # type: int
|
||||||
predicate.categories = dict()
|
predicate.categories = dict() # type: Dict[str, Any]
|
||||||
return predicate
|
return predicate
|
||||||
|
|
||||||
|
|
||||||
def prettify_bug(prefix, output_dir):
|
def prettify_bug(prefix, output_dir):
|
||||||
|
# type: (str, str) -> Callable[[Dict[str, Any]], Dict[str, str]]
|
||||||
def predicate(bug):
|
def predicate(bug):
|
||||||
|
# type: (Dict[str, Any]) -> Dict[str, str]
|
||||||
""" Make safe this values to embed into HTML. """
|
""" Make safe this values to embed into HTML. """
|
||||||
|
|
||||||
bug['bug_type_class'] = category_type_name(bug)
|
bug['bug_type_class'] = category_type_name(bug)
|
||||||
@@ -435,7 +454,9 @@ def prettify_bug(prefix, output_dir):
|
|||||||
|
|
||||||
|
|
||||||
def prettify_crash(prefix, output_dir):
|
def prettify_crash(prefix, output_dir):
|
||||||
|
# type: (str, str) -> Callable[[Dict[str, str]], Dict[str, str]]
|
||||||
def predicate(crash):
|
def predicate(crash):
|
||||||
|
# type: (Dict[str, str]) -> Dict[str, str]
|
||||||
""" Make safe this values to embed into HTML. """
|
""" Make safe this values to embed into HTML. """
|
||||||
|
|
||||||
encode_value(crash, 'source', lambda x: escape(chop(prefix, x)))
|
encode_value(crash, 'source', lambda x: escape(chop(prefix, x)))
|
||||||
@@ -449,6 +470,7 @@ def prettify_crash(prefix, output_dir):
|
|||||||
|
|
||||||
|
|
||||||
def copy_resource_files(output_dir):
|
def copy_resource_files(output_dir):
|
||||||
|
# type: (str) -> None
|
||||||
""" Copy the javascript and css files to the report directory. """
|
""" Copy the javascript and css files to the report directory. """
|
||||||
|
|
||||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
@@ -457,6 +479,7 @@ def copy_resource_files(output_dir):
|
|||||||
|
|
||||||
|
|
||||||
def encode_value(container, key, encode):
|
def encode_value(container, key, encode):
|
||||||
|
# type: (Dict[str, Any], str, Callable[[Any], Any]) -> None
|
||||||
""" Run 'encode' on 'container[key]' value and update it. """
|
""" Run 'encode' on 'container[key]' value and update it. """
|
||||||
|
|
||||||
if key in container:
|
if key in container:
|
||||||
@@ -465,12 +488,14 @@ def encode_value(container, key, encode):
|
|||||||
|
|
||||||
|
|
||||||
def chop(prefix, filename):
|
def chop(prefix, filename):
|
||||||
|
# type: (str, str) -> str
|
||||||
""" Create 'filename' from '/prefix/filename' """
|
""" Create 'filename' from '/prefix/filename' """
|
||||||
|
|
||||||
return filename if not prefix else os.path.relpath(filename, prefix)
|
return filename if not prefix else os.path.relpath(filename, prefix)
|
||||||
|
|
||||||
|
|
||||||
def escape(text):
|
def escape(text):
|
||||||
|
# type: (str) -> str
|
||||||
""" Paranoid HTML escape method. (Python version independent) """
|
""" Paranoid HTML escape method. (Python version independent) """
|
||||||
|
|
||||||
escape_table = {
|
escape_table = {
|
||||||
@@ -484,6 +509,7 @@ def escape(text):
|
|||||||
|
|
||||||
|
|
||||||
def reindent(text, indent):
|
def reindent(text, indent):
|
||||||
|
# type: (str, int) -> str
|
||||||
""" Utility function to format html output and keep indentation. """
|
""" Utility function to format html output and keep indentation. """
|
||||||
|
|
||||||
result = ''
|
result = ''
|
||||||
@@ -494,6 +520,7 @@ def reindent(text, indent):
|
|||||||
|
|
||||||
|
|
||||||
def comment(name, opts=None):
|
def comment(name, opts=None):
|
||||||
|
# type: (str, Dict[str, str]) -> str
|
||||||
""" Utility function to format meta information as comment. """
|
""" Utility function to format meta information as comment. """
|
||||||
|
|
||||||
attributes = ''
|
attributes = ''
|
||||||
@@ -505,6 +532,7 @@ def comment(name, opts=None):
|
|||||||
|
|
||||||
|
|
||||||
def commonprefix_from(filename):
|
def commonprefix_from(filename):
|
||||||
|
# type: (str) -> str
|
||||||
""" Create file prefix from a compilation database entries. """
|
""" Create file prefix from a compilation database entries. """
|
||||||
|
|
||||||
with open(filename, 'r') as handle:
|
with open(filename, 'r') as handle:
|
||||||
@@ -512,6 +540,7 @@ def commonprefix_from(filename):
|
|||||||
|
|
||||||
|
|
||||||
def commonprefix(files):
|
def commonprefix(files):
|
||||||
|
# type: (Iterator[str]) -> str
|
||||||
""" Fixed version of os.path.commonprefix.
|
""" Fixed version of os.path.commonprefix.
|
||||||
|
|
||||||
:param files: list of file names.
|
:param files: list of file names.
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ class AnalyzerTest(unittest.TestCase):
|
|||||||
'source': 'test.java',
|
'source': 'test.java',
|
||||||
'language': 'java'
|
'language': 'java'
|
||||||
}
|
}
|
||||||
self.assertIsNone(sut.language_check(input, spy.call))
|
self.assertEquals(dict(), sut.language_check(input, spy.call))
|
||||||
self.assertIsNone(spy.arg)
|
self.assertIsNone(spy.arg)
|
||||||
|
|
||||||
def test_set_language_sets_flags(self):
|
def test_set_language_sets_flags(self):
|
||||||
@@ -283,7 +283,7 @@ class AnalyzerTest(unittest.TestCase):
|
|||||||
def stop(archs):
|
def stop(archs):
|
||||||
spy = Spy()
|
spy = Spy()
|
||||||
input = {'flags': [], 'arch_list': archs}
|
input = {'flags': [], 'arch_list': archs}
|
||||||
self.assertIsNone(sut.arch_check(input, spy.call))
|
self.assertEqual(dict(), sut.arch_check(input, spy.call))
|
||||||
self.assertIsNone(spy.arg)
|
self.assertIsNone(spy.arg)
|
||||||
|
|
||||||
stop(['ppc'])
|
stop(['ppc'])
|
||||||
|
|||||||
Reference in New Issue
Block a user