Initial check in
Bug: 137197907
diff --git a/src/llvm-project/clang/tools/scan-build-py/libscanbuild/clang.py b/src/llvm-project/clang/tools/scan-build-py/libscanbuild/clang.py
new file mode 100644
index 0000000..0cbfdb6
--- /dev/null
+++ b/src/llvm-project/clang/tools/scan-build-py/libscanbuild/clang.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+""" This module is responsible for the Clang executable.
+
+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. """
+
+import subprocess
+import re
+from libscanbuild import run_command
+from libscanbuild.shell import decode
+
+__all__ = ['get_version', 'get_arguments', 'get_checkers', 'is_ctu_capable',
+ 'get_triple_arch']
+
+# regex for activated checker
+ACTIVE_CHECKER_PATTERN = re.compile(r'^-analyzer-checker=(.*)$')
+
+
+def get_version(clang):
+ """ Returns the compiler version as string.
+
+ :param clang: the compiler we are using
+ :return: the version string printed to stderr """
+
+ output = run_command([clang, '-v'])
+ # the relevant version info is in the first line
+ return output[0]
+
+
+def get_arguments(command, cwd):
+ """ Capture Clang invocation.
+
+ :param command: the compilation command
+ :param cwd: the current working directory
+ :return: the detailed front-end invocation command """
+
+ cmd = command[:]
+ cmd.insert(1, '-###')
+
+ output = run_command(cmd, cwd=cwd)
+ # The relevant information is in the last line of the output.
+ # Don't check if finding last line fails, would throw exception anyway.
+ last_line = output[-1]
+ if re.search(r'clang(.*): error:', last_line):
+ raise Exception(last_line)
+ return decode(last_line)
+
+
+def get_active_checkers(clang, plugins):
+ """ Get the active checker list.
+
+ :param clang: the compiler we are using
+ :param plugins: list of plugins which was requested by the user
+ :return: list of checker names which are active
+
+ To get the default checkers we execute Clang to print how this
+ compilation would be called. And take out the enabled checker from the
+ arguments. For input file we specify stdin and pass only language
+ information. """
+
+ def get_active_checkers_for(language):
+ """ Returns a list of active checkers for the given language. """
+
+ load_args = [arg
+ for plugin in plugins
+ for arg in ['-Xclang', '-load', '-Xclang', plugin]]
+ cmd = [clang, '--analyze'] + load_args + ['-x', language, '-']
+ return [ACTIVE_CHECKER_PATTERN.match(arg).group(1)
+ for arg in get_arguments(cmd, '.')
+ if ACTIVE_CHECKER_PATTERN.match(arg)]
+
+ result = set()
+ for language in ['c', 'c++', 'objective-c', 'objective-c++']:
+ result.update(get_active_checkers_for(language))
+ return frozenset(result)
+
+
+def is_active(checkers):
+ """ Returns a method, which classifies the checker active or not,
+ based on the received checker name list. """
+
+ def predicate(checker):
+ """ Returns True if the given checker is active. """
+
+ return any(pattern.match(checker) for pattern in predicate.patterns)
+
+ predicate.patterns = [re.compile(r'^' + a + r'(\.|$)') for a in checkers]
+ return predicate
+
+
+def parse_checkers(stream):
+ """ Parse clang -analyzer-checker-help output.
+
+ Below the line 'CHECKERS:' are there the name description pairs.
+ Many of them are in one line, but some long named checker has the
+ name and the description in separate lines.
+
+ The checker name is always prefixed with two space character. The
+ name contains no whitespaces. Then followed by newline (if it's
+ too long) or other space characters comes the description of the
+ checker. The description ends with a newline character.
+
+ :param stream: list of lines to parse
+ :return: generator of tuples
+
+ (<checker name>, <checker description>) """
+
+ lines = iter(stream)
+ # find checkers header
+ for line in lines:
+ if re.match(r'^CHECKERS:', line):
+ break
+ # find entries
+ state = None
+ for line in lines:
+ if state and not re.match(r'^\s\s\S', line):
+ yield (state, line.strip())
+ state = None
+ elif re.match(r'^\s\s\S+$', line.rstrip()):
+ state = line.strip()
+ else:
+ pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)')
+ match = pattern.match(line.rstrip())
+ if match:
+ current = match.groupdict()
+ yield (current['key'], current['value'])
+
+
+def get_checkers(clang, plugins):
+ """ Get all the available checkers from default and from the plugins.
+
+ :param clang: the compiler we are using
+ :param plugins: list of plugins which was requested by the user
+ :return: a dictionary of all available checkers and its status
+
+ {<checker name>: (<checker description>, <is active by default>)} """
+
+ load = [elem for plugin in plugins for elem in ['-load', plugin]]
+ cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help']
+
+ lines = run_command(cmd)
+
+ is_active_checker = is_active(get_active_checkers(clang, plugins))
+
+ checkers = {
+ name: (description, is_active_checker(name))
+ for name, description in parse_checkers(lines)
+ }
+ if not checkers:
+ raise Exception('Could not query Clang for available checkers.')
+
+ return checkers
+
+
+def is_ctu_capable(extdef_map_cmd):
+ """ Detects if the current (or given) clang and external definition mapping
+ executables are CTU compatible. """
+
+ try:
+ run_command([extdef_map_cmd, '-version'])
+ except (OSError, subprocess.CalledProcessError):
+ return False
+ return True
+
+
+def get_triple_arch(command, cwd):
+ """Returns the architecture part of the target triple for the given
+ compilation command. """
+
+ cmd = get_arguments(command, cwd)
+ try:
+ separator = cmd.index("-triple")
+ return cmd[separator + 1]
+ except (IndexError, ValueError):
+ return ""