diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py
--- a/clang/utils/analyzer/SATestBuild.py
+++ b/clang/utils/analyzer/SATestBuild.py
@@ -60,8 +60,8 @@
 
 from queue import Queue
 from subprocess import CalledProcessError, check_call
-from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Tuple,
-                    TYPE_CHECKING)
+from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Optional,
+                    Tuple, TYPE_CHECKING)
 
 
 ###############################################################################
@@ -93,13 +93,15 @@
 
 # Find Clang for static analysis.
 if 'CC' in os.environ:
-    CLANG = os.environ['CC']
+    cc_candidate: Optional[str] = os.environ['CC']
 else:
-    CLANG = SATestUtils.which("clang", os.environ['PATH'])
-if not CLANG:
+    cc_candidate = SATestUtils.which("clang", os.environ['PATH'])
+if not cc_candidate:
     stderr("Error: cannot find 'clang' in PATH")
     sys.exit(1)
 
+CLANG = cc_candidate
+
 # Number of jobs.
 MAX_JOBS = int(math.ceil(multiprocessing.cpu_count() * 0.75))
 
@@ -204,8 +206,9 @@
     cwd = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
     script_path = os.path.join(directory, CLEANUP_SCRIPT)
 
-    SATestUtils.runScript(script_path, build_log_file, cwd,
-                          Stdout=LOCAL.stdout, Stderr=LOCAL.stderr)
+    SATestUtils.run_script(script_path, build_log_file, cwd,
+                           out=LOCAL.stdout, err=LOCAL.stderr,
+                           verbose=VERBOSE)
 
 
 def download_and_patch(directory: str, build_log_file: IO):
@@ -238,8 +241,9 @@
     Run the script to download the project, if it exists.
     """
     script_path = os.path.join(directory, DOWNLOAD_SCRIPT)
-    SATestUtils.runScript(script_path, build_log_file, directory,
-                          Stdout=LOCAL.stdout, Stderr=LOCAL.stderr)
+    SATestUtils.run_script(script_path, build_log_file, directory,
+                           out=LOCAL.stdout, err=LOCAL.stderr,
+                           verbose=VERBOSE)
 
 
 def apply_patch(directory: str, build_log_file: IO):
@@ -549,9 +553,9 @@
             failed = False
 
             # Only run the analyzes on supported files.
-            if SATestUtils.hasNoExtension(file_name):
+            if SATestUtils.has_no_extension(file_name):
                 continue
-            if not SATestUtils.isValidSingleInputFile(file_name):
+            if not SATestUtils.is_valid_single_input_file(file_name):
                 stderr(f"Error: Invalid single input file {full_file_name}.\n")
                 raise Exception()
 
@@ -851,7 +855,7 @@
     map_file.seek(0)
     # TODO: csv format is not very readable, change it to JSON
     for project_info in csv.reader(map_file):
-        if (SATestUtils.isCommentCSVLine(project_info)):
+        if SATestUtils.is_comment_csv_line(project_info):
             continue
         # suppress mypy error
         yield cast(Tuple[str, str], project_info)
diff --git a/clang/utils/analyzer/SATestUtils.py b/clang/utils/analyzer/SATestUtils.py
--- a/clang/utils/analyzer/SATestUtils.py
+++ b/clang/utils/analyzer/SATestUtils.py
@@ -1,12 +1,11 @@
 import os
-from subprocess import check_call
 import sys
 
+from subprocess import CalledProcessError, check_call
+from typing import List, IO, Optional
 
-Verbose = 1
 
-
-def which(command, paths=None):
+def which(command: str, paths: Optional[str] = None) -> Optional[str]:
     """which(command, [paths]) - Look up the given command in the paths string
     (or the PATH environment variable, if unspecified)."""
 
@@ -38,41 +37,41 @@
     return None
 
 
-def hasNoExtension(FileName):
-    (Root, Ext) = os.path.splitext(FileName)
-    return (Ext == "")
+def has_no_extension(file_name: str) -> bool:
+    root, ext = os.path.splitext(file_name)
+    return ext == ""
 
 
-def isValidSingleInputFile(FileName):
-    (Root, Ext) = os.path.splitext(FileName)
-    return Ext in (".i", ".ii", ".c", ".cpp", ".m", "")
+def is_valid_single_input_file(file_name):
+    root, ext = os.path.splitext(file_name)
+    return ext in (".i", ".ii", ".c", ".cpp", ".m", "")
 
 
-def runScript(ScriptPath, PBuildLogFile, Cwd, Stdout=sys.stdout,
-              Stderr=sys.stderr):
+def run_script(script_path: str, build_log_file: IO, cwd: str,
+               out=sys.stdout, err=sys.stderr, verbose: int = 0):
     """
     Run the provided script if it exists.
     """
-    if os.path.exists(ScriptPath):
+    if os.path.exists(script_path):
         try:
-            if Verbose == 1:
-                Stdout.write("  Executing: %s\n" % (ScriptPath,))
-            check_call("chmod +x '%s'" % ScriptPath, cwd=Cwd,
-                       stderr=PBuildLogFile,
-                       stdout=PBuildLogFile,
+            if verbose == 1:
+                out.write("  Executing: %s\n" % (script_path,))
+            check_call("chmod +x '%s'" % script_path, cwd=cwd,
+                       stderr=build_log_file,
+                       stdout=build_log_file,
                        shell=True)
-            check_call("'%s'" % ScriptPath, cwd=Cwd,
-                       stderr=PBuildLogFile,
-                       stdout=PBuildLogFile,
+            check_call("'%s'" % script_path, cwd=cwd,
+                       stderr=build_log_file,
+                       stdout=build_log_file,
                        shell=True)
-        except:
-            Stderr.write("Error: Running %s failed. See %s for details.\n" % (
-                         ScriptPath, PBuildLogFile.name))
+        except CalledProcessError:
+            err.write("Error: Running %s failed. See %s for details.\n" % (
+                         script_path, build_log_file.name))
             sys.exit(-1)
 
 
-def isCommentCSVLine(Entries):
+def is_comment_csv_line(entries: List[str]) -> bool:
     """
     Treat CSV lines starting with a '#' as a comment.
     """
-    return len(Entries) > 0 and Entries[0].startswith("#")
+    return len(entries) > 0 and entries[0].startswith("#")