[PATCH] D80171: [analyzer] LoopUnrolling: fix crash when a parameter is a loop counter

2020-05-20 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp:167
 
+  const bool isParm = VD->getKind() == Decl::ParmVar;
+  // Reference parameters are assumed as escaped variables.

`getKind` function is only an implementation detail for `isa`/`cast`/`dan_cast` 
functions ([[ 
https://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates
 | docs ]]).  So, this condition would be better in the following form: 
`isa(VD)`.

NOTE: One good motivation here is that //maybe// in the future there will be 
some sort of new type of function parameters and it will be derived from 
`ParmVarDecl`.  In this situation, direct comparison with the kind of node will 
not work and probably won't be fixed by developers who introduced the new node, 
but `isa` approach will stay correct.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80171/new/

https://reviews.llvm.org/D80171



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D75432: [analyzer][NFC][MallocChecker] Convert many parameters into CallEvent

2020-05-20 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Hi, @Szelethus, I don't know exactly which of the changes (this one, 
https://reviews.llvm.org/D75430, or https://reviews.llvm.org/D75431) causes a 
crash on SQLite, but it's definitely one of these.

**Steps to reproduce**

  clang -cc1 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage 
-Werror=implicit-function-declaration -analyze -disable-free -main-file-name 
sqlite3.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks 
-analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix 
-analyzer-checker=osx 
-analyzer-checker=security.insecureAPI.decodeValueOfObjCType 
-analyzer-checker=deadcode 
-analyzer-checker=security.insecureAPI.UncheckedReturn 
-analyzer-checker=security.insecureAPI.getpw 
-analyzer-checker=security.insecureAPI.gets 
-analyzer-checker=security.insecureAPI.mktemp 
-analyzer-checker=security.insecureAPI.mkstemp 
-analyzer-checker=security.insecureAPI.vfork 
-analyzer-checker=nullability.NullPassedToNonnull 
-analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w 
-setup-static-analyzer -analyzer-config-compatibility-mode=true 
-mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all 
-fno-strict-return -fno-rounding-math -munwind-tables 
-faligned-alloc-unavailable -target-cpu core2 -dwarf-column-info 
-target-linker-version 556.6 -Wno-reorder-init-list 
-Wno-implicit-int-float-conversion -Wno-c99-designator 
-Wno-final-dtor-non-final-class -Wno-extra-semi-stmt 
-Wno-misleading-indentation -Wno-quoted-include-in-framework-header 
-Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion 
-ferror-limit 19 -stack-protector 1 -fblocks -fencode-extended-block-signature 
-fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fmax-type-align=16 
-analyzer-checker=alpha.unix.SimpleStream,alpha.security.taint,cplusplus.NewDeleteLeaks,core,cplusplus,deadcode,security,unix,osx,nullability
 -analyzer-config serialize-stats=true,stable-report-filename=true -x c 
sqlite3-258aa5.c

**Output**

  Assertion failed: (FromPtr && ToPtr && "By this point, FreeMemAux and 
MallocMemAux should have checked " "whether the argument or the return value is 
symbolic!"), function ReallocMemAux, file 
/Users/vsavchenko/source/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp,
 line 2409.

Attached file is the exact version of SQLite source code to reproduce the 
issue: F11965188: sqlite3-258aa5.c 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75432/new/

https://reviews.llvm.org/D75432



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80171: [analyzer] LoopUnrolling: fix crash when a parameter is a loop counter

2020-05-20 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko accepted this revision.
vsavchenko added a comment.

LGTM! Thanks again!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80171/new/

https://reviews.llvm.org/D80171



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77802: [analyzer] Improved RangeSet::Negate support of unsigned ranges

2020-05-21 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

@NoQ @ASDenysPetrov I will rebase my changes - no worries :)


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77802/new/

https://reviews.llvm.org/D77802



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80423: [analyzer] SATestBuild.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

SATest scripts should be more python-style than they are now.
This includes better architecture, type annotations, naming
convesions, and up-to-date language features.  This commit starts
with two scripts SATestBuild and SATestAdd.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80423

Files:
  clang/utils/analyzer/SATestAdd.py
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -21,21 +21,21 @@
 
 
 def updateReferenceResults(ProjName, ProjBuildMode):
-ProjDir = SATestBuild.getProjectDir(ProjName)
+ProjTester = SATestBuild.ProjectTester(ProjName, ProjBuildMode)
+ProjDir = ProjTester.get_project_dir()
 
-RefResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=True))
-CreatedResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=False))
+ProjTester.is_reference_build = True
+RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+
+ProjTester.is_reference_build = False
+CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
 
 if not os.path.exists(CreatedResultsPath):
 print("New results not found, was SATestBuild.py "
   "previously run?", file=sys.stderr)
 sys.exit(1)
 
-BuildLogPath = SATestBuild.getBuildLogPath(RefResultsPath)
+BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath)
 Dirname = os.path.dirname(os.path.abspath(BuildLogPath))
 runCmd("mkdir -p '%s'" % Dirname)
 with open(BuildLogPath, "w+") as PBuildLogFile:
@@ -50,13 +50,13 @@
stdout=PBuildLogFile)
 
 # Run cleanup script.
-SATestBuild.runCleanupScript(ProjDir, PBuildLogFile)
+SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile)
 
-SATestBuild.normalizeReferenceResults(
+SATestBuild.normalize_reference_results(
 ProjDir, RefResultsPath, ProjBuildMode)
 
 # Clean up the generated difference results.
-SATestBuild.cleanupReferenceResults(RefResultsPath)
+SATestBuild.cleanup_reference_results(RefResultsPath)
 
 runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
 
@@ -69,8 +69,8 @@
   file=sys.stderr)
 sys.exit(1)
 
-with SATestBuild.projectFileHandler() as f:
-for (ProjName, ProjBuildMode) in SATestBuild.iterateOverProjects(f):
+with open(SATestBuild.get_project_map_path(), "r") as f:
+for ProjName, ProjBuildMode in SATestBuild.get_projects(f):
 updateReferenceResults(ProjName, int(ProjBuildMode))
 
 
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -45,7 +45,6 @@
 import CmpRuns
 import SATestUtils
 
-from subprocess import CalledProcessError, check_call
 import argparse
 import csv
 import glob
@@ -59,59 +58,33 @@
 import threading
 import time
 
-try:
-import queue
-except ImportError:
-import Queue as queue
+from queue import Queue
+from subprocess import CalledProcessError, check_call
+from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Tuple,
+TYPE_CHECKING)
+
 
 ###
 # Helper functions.
 ###
 
-Local = threading.local()
-Local.stdout = sys.stdout
-Local.stderr = sys.stderr
-logging.basicConfig(
-level=logging.DEBUG,
-format='%(asctime)s:%(levelname)s:%(name)s: %(message)s')
-
+LOCAL = threading.local()
+LOCAL.stdout = sys.stdout
+LOCAL.stderr = sys.stderr
 
-class StreamToLogger:
-def __init__(self, logger, log_level=logging.INFO):
-self.logger = logger
-self.log_level = log_level
 
-def write(self, buf):
-# Rstrip in order not to write an extra newline.
-self.logger.log(self.log_level, buf.rstrip())
+def stderr(message: str):
+LOCAL.stderr.write(message)
 
-def flush(self):
-pass
 
-def fileno(self):
-return 0
+def stdout(message: str):
+LOCAL.stdout.write(message)
 
 
-def getProjectMapPath():
-ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
-  ProjectMapFile)
-if not os.path.exists(ProjectMapPath):
-Local.stdout.write("Error: Cannot find the Project Map 

[PATCH] D80424: [analyzer] SATestUtils.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 265680.
vsavchenko added a comment.

Modify forgotten parts


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80424/new/

https://reviews.llvm.org/D80424

Files:
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUtils.py

Index: clang/utils/analyzer/SATestUtils.py
===
--- clang/utils/analyzer/SATestUtils.py
+++ 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,44 @@
 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: str) -> bool:
+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(f"  Executing: {script_path}\n")
+
+check_call(f"chmod +x '{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(f"'{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(f"Error: Running {script_path} failed. "
+  f"See {build_log_file.name} for details.\n")
 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("#")
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ 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,

[PATCH] D80424: [analyzer] SATestUtils.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.
vsavchenko updated this revision to Diff 265680.
vsavchenko added a comment.
vsavchenko added a parent revision: D80423: [analyzer] SATestBuild.py: Refactor 
and add type annotations.

Modify forgotten parts


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80424

Files:
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUtils.py

Index: clang/utils/analyzer/SATestUtils.py
===
--- clang/utils/analyzer/SATestUtils.py
+++ 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,44 @@
 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: str) -> bool:
+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(f"  Executing: {script_path}\n")
+
+check_call(f"chmod +x '{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(f"'{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(f"Error: Running {script_path} failed. "
+  f"See {build_log_file.name} for details.\n")
 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("#")
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ 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(scr

[PATCH] D80423: [analyzer] SATestBuild.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 265682.
vsavchenko added a comment.

Fix update diffs


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80423/new/

https://reviews.llvm.org/D80423

Files:
  clang/utils/analyzer/SATestAdd.py
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -21,21 +21,22 @@
 
 
 def updateReferenceResults(ProjName, ProjBuildMode):
-ProjDir = SATestBuild.getProjectDir(ProjName)
+ProjInfo = SATestBuild.ProjectInfo(ProjName, ProjBuildMode)
+ProjTester = SATestBuild.ProjectTester(ProjInfo)
+ProjDir = ProjTester.get_project_dir()
 
-RefResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=True))
-CreatedResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=False))
+ProjTester.is_reference_build = True
+RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+
+ProjTester.is_reference_build = False
+CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
 
 if not os.path.exists(CreatedResultsPath):
 print("New results not found, was SATestBuild.py "
   "previously run?", file=sys.stderr)
 sys.exit(1)
 
-BuildLogPath = SATestBuild.getBuildLogPath(RefResultsPath)
+BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath)
 Dirname = os.path.dirname(os.path.abspath(BuildLogPath))
 runCmd("mkdir -p '%s'" % Dirname)
 with open(BuildLogPath, "w+") as PBuildLogFile:
@@ -50,13 +51,13 @@
stdout=PBuildLogFile)
 
 # Run cleanup script.
-SATestBuild.runCleanupScript(ProjDir, PBuildLogFile)
+SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile)
 
-SATestBuild.normalizeReferenceResults(
+SATestBuild.normalize_reference_results(
 ProjDir, RefResultsPath, ProjBuildMode)
 
 # Clean up the generated difference results.
-SATestBuild.cleanupReferenceResults(RefResultsPath)
+SATestBuild.cleanup_reference_results(RefResultsPath)
 
 runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
 
@@ -69,8 +70,8 @@
   file=sys.stderr)
 sys.exit(1)
 
-with SATestBuild.projectFileHandler() as f:
-for (ProjName, ProjBuildMode) in SATestBuild.iterateOverProjects(f):
+with open(SATestBuild.get_project_map_path(), "r") as f:
+for ProjName, ProjBuildMode in SATestBuild.get_projects(f):
 updateReferenceResults(ProjName, int(ProjBuildMode))
 
 
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -45,7 +45,6 @@
 import CmpRuns
 import SATestUtils
 
-from subprocess import CalledProcessError, check_call
 import argparse
 import csv
 import glob
@@ -59,59 +58,33 @@
 import threading
 import time
 
-try:
-import queue
-except ImportError:
-import Queue as queue
+from queue import Queue
+from subprocess import CalledProcessError, check_call
+from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Tuple,
+TYPE_CHECKING)
+
 
 ###
 # Helper functions.
 ###
 
-Local = threading.local()
-Local.stdout = sys.stdout
-Local.stderr = sys.stderr
-logging.basicConfig(
-level=logging.DEBUG,
-format='%(asctime)s:%(levelname)s:%(name)s: %(message)s')
-
+LOCAL = threading.local()
+LOCAL.stdout = sys.stdout
+LOCAL.stderr = sys.stderr
 
-class StreamToLogger:
-def __init__(self, logger, log_level=logging.INFO):
-self.logger = logger
-self.log_level = log_level
 
-def write(self, buf):
-# Rstrip in order not to write an extra newline.
-self.logger.log(self.log_level, buf.rstrip())
+def stderr(message: str):
+LOCAL.stderr.write(message)
 
-def flush(self):
-pass
 
-def fileno(self):
-return 0
+def stdout(message: str):
+LOCAL.stdout.write(message)
 
 
-def getProjectMapPath():
-ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
-  ProjectMapFile)
-if not os.path.exists(ProjectMapPath):
-Local.stdout.write("Error: Cannot find the Project Map file " +
-   ProjectMapPath +
-   "\nRunning script for the wrong directory?\n")
-sys.exit(1)
-return ProjectMapPath
-
-
-def getProjectDir(ID):
-return os.path.join(os.path.abspath(os.curdir), ID)
-
+logging.basicConfig(
+leve

[PATCH] D80426: [analyzer] SATestUpdateDiffs.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.
vsavchenko added a parent revision: D80424: [analyzer] SATestUtils.py: Refactor 
and add type annotations.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80426

Files:
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -3,67 +3,67 @@
 """
 Update reference results for static analyzer.
 """
-from __future__ import absolute_import, division, print_function
-
 import SATestBuild
 
-from subprocess import check_call
 import os
+import shutil
 import sys
 
+from subprocess import check_call
+
 Verbose = 0
 
 
-def runCmd(Command, **kwargs):
-if Verbose:
-print("Executing %s" % Command)
-check_call(Command, shell=True, **kwargs)
+def update_reference_results(project_name: str, build_mode: int):
+project_info = SATestBuild.ProjectInfo(project_name, build_mode)
+tester = SATestBuild.ProjectTester(project_info)
+project_dir = tester.get_project_dir()
 
+tester.is_reference_build = True
+ref_results_path = os.path.join(project_dir, tester.get_output_dir())
 
-def updateReferenceResults(ProjName, ProjBuildMode):
-ProjInfo = SATestBuild.ProjectInfo(ProjName, ProjBuildMode)
-ProjTester = SATestBuild.ProjectTester(ProjInfo)
-ProjDir = ProjTester.get_project_dir()
+tester.is_reference_build = False
+created_results_path = os.path.join(project_dir, tester.get_output_dir())
 
-ProjTester.is_reference_build = True
-RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+if not os.path.exists(created_results_path):
+print("New results not found, was SATestBuild.py previously run?",
+  file=sys.stderr)
+sys.exit(1)
 
-ProjTester.is_reference_build = False
-CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+build_log_path = SATestBuild.get_build_log_path(ref_results_path)
+build_log_dir = os.path.dirname(os.path.abspath(build_log_path))
 
-if not os.path.exists(CreatedResultsPath):
-print("New results not found, was SATestBuild.py "
-  "previously run?", file=sys.stderr)
-sys.exit(1)
+os.makedirs(build_log_dir)
+
+with open(build_log_path, "w+") as build_log_file:
+def run_cmd(command: str):
+if Verbose:
+print(f"Executing {command}")
+check_call(command, shell=True, stdout=build_log_file)
 
-BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath)
-Dirname = os.path.dirname(os.path.abspath(BuildLogPath))
-runCmd("mkdir -p '%s'" % Dirname)
-with open(BuildLogPath, "w+") as PBuildLogFile:
 # Remove reference results: in git, and then again for a good measure
 # with rm, as git might not remove things fully if there are empty
 # directories involved.
-runCmd('git rm -r -q "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
-runCmd('rm -rf "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
+run_cmd(f"git rm -r -q '{ref_results_path}'")
+shutil.rmtree(ref_results_path)
 
 # Replace reference results with a freshly computed once.
-runCmd('cp -r "%s" "%s"' % (CreatedResultsPath, RefResultsPath,),
-   stdout=PBuildLogFile)
+shutil.copytree(created_results_path, ref_results_path, symlinks=True)
 
 # Run cleanup script.
-SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile)
+SATestBuild.run_cleanup_script(project_dir, build_log_file)
 
 SATestBuild.normalize_reference_results(
-ProjDir, RefResultsPath, ProjBuildMode)
+project_dir, ref_results_path, build_mode)
 
 # Clean up the generated difference results.
-SATestBuild.cleanup_reference_results(RefResultsPath)
+SATestBuild.cleanup_reference_results(ref_results_path)
 
-runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
+run_cmd(f"git add '{ref_results_path}'")
 
 
 def main(argv):
-if len(argv) == 2 and argv[1] in ('-h', '--help'):
+if len(argv) == 2 and argv[1] in ("-h", "--help"):
 print("Update static analyzer reference results based "
   "\non the previous run of SATestBuild.py.\n"
   "\nN.B.: Assumes that SATestBuild.py was just run",
@@ -71,8 +71,8 @@
 sys.exit(1)
 
 with open(SATestBuild.get_project_map_path(), "r") as f:
-for ProjName, ProjBuildMode in SATestBuild.get_projects(f):
-updateReferenceResults(ProjName, int(ProjBuildMode))
+for project_name, build_mode i

[PATCH] D80427: [analyzer] SumTimerInfo.py: Partially modernize

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80427

Files:
  clang/utils/analyzer/SumTimerInfo.py

Index: clang/utils/analyzer/SumTimerInfo.py
===
--- clang/utils/analyzer/SumTimerInfo.py
+++ clang/utils/analyzer/SumTimerInfo.py
@@ -6,8 +6,6 @@
 Statistics are enabled by passing '-internal-stats' option to scan-build
 (or '-analyzer-stats' to the analyzer).
 """
-from __future__ import absolute_import, division, print_function
-
 import sys
 
 if __name__ == '__main__':
@@ -17,64 +15,65 @@
 sys.exit(-1)
 
 f = open(sys.argv[1], 'r')
-Time = 0.0
-TotalTime = 0.0
-MaxTime = 0.0
-Warnings = 0
-Count = 0
-FunctionsAnalyzed = 0
-ReachableBlocks = 0
-ReachedMaxSteps = 0
-NumSteps = 0
-NumInlinedCallSites = 0
-NumBifurcatedCallSites = 0
-MaxCFGSize = 0
+time = 0.0
+total_time = 0.0
+max_time = 0.0
+warnings = 0
+count = 0
+functions_analyzed = 0
+reachable_blocks = 0
+reached_max_steps = 0
+num_steps = 0
+num_inlined_call_sites = 0
+num_bifurcated_call_sites = 0
+max_cfg_size = 0
+
 for line in f:
-if ("Analyzer Total Time" in line):
+if "Analyzer total time" in line:
 s = line.split()
-Time = Time + float(s[6])
-Count = Count + 1
-if (float(s[6]) > MaxTime):
-MaxTime = float(s[6])
-if ("warning generated." in line) or ("warnings generated" in line):
+time = time + float(s[6])
+count = count + 1
+if float(s[6]) > max_time:
+max_time = float(s[6])
+if "warning generated." in line or "warnings generated" in line:
 s = line.split()
-Warnings = Warnings + int(s[0])
+warnings = warnings + int(s[0])
 if "The # of functions analysed (as top level)" in line:
 s = line.split()
-FunctionsAnalyzed = FunctionsAnalyzed + int(s[0])
+functions_analyzed = functions_analyzed + int(s[0])
 if "The % of reachable basic blocks" in line:
 s = line.split()
-ReachableBlocks = ReachableBlocks + int(s[0])
+reachable_blocks = reachable_blocks + int(s[0])
 if "The # of times we reached the max number of steps" in line:
 s = line.split()
-ReachedMaxSteps = ReachedMaxSteps + int(s[0])
+reached_max_steps = reached_max_steps + int(s[0])
 if "The maximum number of basic blocks in a function" in line:
 s = line.split()
-if MaxCFGSize < int(s[0]):
-MaxCFGSize = int(s[0])
+if max_cfg_size < int(s[0]):
+max_cfg_size = int(s[0])
 if "The # of steps executed" in line:
 s = line.split()
-NumSteps = NumSteps + int(s[0])
+num_steps = num_steps + int(s[0])
 if "The # of times we inlined a call" in line:
 s = line.split()
-NumInlinedCallSites = NumInlinedCallSites + int(s[0])
+num_inlined_call_sites = num_inlined_call_sites + int(s[0])
 if "The # of times we split the path due \
 to imprecise dynamic dispatch info" in line:
 s = line.split()
-NumBifurcatedCallSites = NumBifurcatedCallSites + int(s[0])
+num_bifurcated_call_sites = num_bifurcated_call_sites + int(s[0])
 if ")  Total" in line:
 s = line.split()
-TotalTime = TotalTime + float(s[6])
+total_time = total_time + float(s[6])
 
-print("TU Count %d" % (Count))
-print("Time %f" % (Time))
-print("Warnings %d" % (Warnings))
-print("Functions Analyzed %d" % (FunctionsAnalyzed))
-print("Reachable Blocks %d" % (ReachableBlocks))
-print("Reached Max Steps %d" % (ReachedMaxSteps))
-print("Number of Steps %d" % (NumSteps))
-print("Number of Inlined calls %d (bifurcated %d)" % (
-NumInlinedCallSites, NumBifurcatedCallSites))
-print("MaxTime %f" % (MaxTime))
-print("TotalTime %f" % (TotalTime))
-print("Max CFG Size %d" % (MaxCFGSize))
+print(f"TU count {count}")
+print(f"Time {time}")
+print(f"Warnings {warnings}")
+print(f"Functions analyzed {functions_analyzed}")
+print(f"Reachable blocks {reachable_blocks}")
+print(f"Reached max steps {reached_max_steps}")
+print(f"Number of steps {num_steps}")
+print(f"Number of inlined calls {num_inlined_call_sites} "
+  f"(bifurcated {num_bifurcated_call_sites})")
+print(f"Max time {max_time}")
+print(f"Total time {total_time}")
+print(

[PATCH] D80423: [analyzer] SATestBuild.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 265691.
vsavchenko added a comment.

Fix code review comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80423/new/

https://reviews.llvm.org/D80423

Files:
  clang/utils/analyzer/SATestAdd.py
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -21,21 +21,22 @@
 
 
 def updateReferenceResults(ProjName, ProjBuildMode):
-ProjDir = SATestBuild.getProjectDir(ProjName)
+ProjInfo = SATestBuild.ProjectInfo(ProjName, ProjBuildMode)
+ProjTester = SATestBuild.ProjectTester(ProjInfo)
+ProjDir = ProjTester.get_project_dir()
 
-RefResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=True))
-CreatedResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=False))
+ProjTester.is_reference_build = True
+RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+
+ProjTester.is_reference_build = False
+CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
 
 if not os.path.exists(CreatedResultsPath):
 print("New results not found, was SATestBuild.py "
   "previously run?", file=sys.stderr)
 sys.exit(1)
 
-BuildLogPath = SATestBuild.getBuildLogPath(RefResultsPath)
+BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath)
 Dirname = os.path.dirname(os.path.abspath(BuildLogPath))
 runCmd("mkdir -p '%s'" % Dirname)
 with open(BuildLogPath, "w+") as PBuildLogFile:
@@ -50,13 +51,13 @@
stdout=PBuildLogFile)
 
 # Run cleanup script.
-SATestBuild.runCleanupScript(ProjDir, PBuildLogFile)
+SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile)
 
-SATestBuild.normalizeReferenceResults(
+SATestBuild.normalize_reference_results(
 ProjDir, RefResultsPath, ProjBuildMode)
 
 # Clean up the generated difference results.
-SATestBuild.cleanupReferenceResults(RefResultsPath)
+SATestBuild.cleanup_reference_results(RefResultsPath)
 
 runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
 
@@ -69,8 +70,8 @@
   file=sys.stderr)
 sys.exit(1)
 
-with SATestBuild.projectFileHandler() as f:
-for (ProjName, ProjBuildMode) in SATestBuild.iterateOverProjects(f):
+with open(SATestBuild.get_project_map_path(), "r") as f:
+for ProjName, ProjBuildMode in SATestBuild.get_projects(f):
 updateReferenceResults(ProjName, int(ProjBuildMode))
 
 
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -45,7 +45,6 @@
 import CmpRuns
 import SATestUtils
 
-from subprocess import CalledProcessError, check_call
 import argparse
 import csv
 import glob
@@ -59,59 +58,33 @@
 import threading
 import time
 
-try:
-import queue
-except ImportError:
-import Queue as queue
+from queue import Queue
+from subprocess import CalledProcessError, check_call
+from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Tuple,
+TYPE_CHECKING)
+
 
 ###
 # Helper functions.
 ###
 
-Local = threading.local()
-Local.stdout = sys.stdout
-Local.stderr = sys.stderr
-logging.basicConfig(
-level=logging.DEBUG,
-format='%(asctime)s:%(levelname)s:%(name)s: %(message)s')
-
+LOCAL = threading.local()
+LOCAL.stdout = sys.stdout
+LOCAL.stderr = sys.stderr
 
-class StreamToLogger:
-def __init__(self, logger, log_level=logging.INFO):
-self.logger = logger
-self.log_level = log_level
 
-def write(self, buf):
-# Rstrip in order not to write an extra newline.
-self.logger.log(self.log_level, buf.rstrip())
+def stderr(message: str):
+LOCAL.stderr.write(message)
 
-def flush(self):
-pass
 
-def fileno(self):
-return 0
+def stdout(message: str):
+LOCAL.stdout.write(message)
 
 
-def getProjectMapPath():
-ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
-  ProjectMapFile)
-if not os.path.exists(ProjectMapPath):
-Local.stdout.write("Error: Cannot find the Project Map file " +
-   ProjectMapPath +
-   "\nRunning script for the wrong directory?\n")
-sys.exit(1)
-return ProjectMapPath
-
-
-def getProjectDir(ID):
-return os.path.join(os.path.abspath(os.curdir), ID)
-
+logging.basicConfig(
+

[PATCH] D80423: [analyzer] SATestBuild.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG4902ca6da49b: [analyzer] SATestBuild.py: Refactor and add 
type annotations (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80423/new/

https://reviews.llvm.org/D80423

Files:
  clang/utils/analyzer/SATestAdd.py
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -21,21 +21,22 @@
 
 
 def updateReferenceResults(ProjName, ProjBuildMode):
-ProjDir = SATestBuild.getProjectDir(ProjName)
+ProjInfo = SATestBuild.ProjectInfo(ProjName, ProjBuildMode)
+ProjTester = SATestBuild.ProjectTester(ProjInfo)
+ProjDir = ProjTester.get_project_dir()
 
-RefResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=True))
-CreatedResultsPath = os.path.join(
-ProjDir,
-SATestBuild.getSBOutputDirName(IsReferenceBuild=False))
+ProjTester.is_reference_build = True
+RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+
+ProjTester.is_reference_build = False
+CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
 
 if not os.path.exists(CreatedResultsPath):
 print("New results not found, was SATestBuild.py "
   "previously run?", file=sys.stderr)
 sys.exit(1)
 
-BuildLogPath = SATestBuild.getBuildLogPath(RefResultsPath)
+BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath)
 Dirname = os.path.dirname(os.path.abspath(BuildLogPath))
 runCmd("mkdir -p '%s'" % Dirname)
 with open(BuildLogPath, "w+") as PBuildLogFile:
@@ -50,13 +51,13 @@
stdout=PBuildLogFile)
 
 # Run cleanup script.
-SATestBuild.runCleanupScript(ProjDir, PBuildLogFile)
+SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile)
 
-SATestBuild.normalizeReferenceResults(
+SATestBuild.normalize_reference_results(
 ProjDir, RefResultsPath, ProjBuildMode)
 
 # Clean up the generated difference results.
-SATestBuild.cleanupReferenceResults(RefResultsPath)
+SATestBuild.cleanup_reference_results(RefResultsPath)
 
 runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
 
@@ -69,8 +70,8 @@
   file=sys.stderr)
 sys.exit(1)
 
-with SATestBuild.projectFileHandler() as f:
-for (ProjName, ProjBuildMode) in SATestBuild.iterateOverProjects(f):
+with open(SATestBuild.get_project_map_path(), "r") as f:
+for ProjName, ProjBuildMode in SATestBuild.get_projects(f):
 updateReferenceResults(ProjName, int(ProjBuildMode))
 
 
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -45,7 +45,6 @@
 import CmpRuns
 import SATestUtils
 
-from subprocess import CalledProcessError, check_call
 import argparse
 import csv
 import glob
@@ -59,59 +58,33 @@
 import threading
 import time
 
-try:
-import queue
-except ImportError:
-import Queue as queue
+from queue import Queue
+from subprocess import CalledProcessError, check_call
+from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Tuple,
+TYPE_CHECKING)
+
 
 ###
 # Helper functions.
 ###
 
-Local = threading.local()
-Local.stdout = sys.stdout
-Local.stderr = sys.stderr
-logging.basicConfig(
-level=logging.DEBUG,
-format='%(asctime)s:%(levelname)s:%(name)s: %(message)s')
-
+LOCAL = threading.local()
+LOCAL.stdout = sys.stdout
+LOCAL.stderr = sys.stderr
 
-class StreamToLogger:
-def __init__(self, logger, log_level=logging.INFO):
-self.logger = logger
-self.log_level = log_level
 
-def write(self, buf):
-# Rstrip in order not to write an extra newline.
-self.logger.log(self.log_level, buf.rstrip())
+def stderr(message: str):
+LOCAL.stderr.write(message)
 
-def flush(self):
-pass
 
-def fileno(self):
-return 0
+def stdout(message: str):
+LOCAL.stdout.write(message)
 
 
-def getProjectMapPath():
-ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
-  ProjectMapFile)
-if not os.path.exists(ProjectMapPath):
-Local.stdout.write("Error: Cannot find the Project Map file " +
-   ProjectMapPath +
-   "\nRunning script for the wrong directory?\n")
-sys.exit(1)
-return ProjectMapPath
-
-
-def getProject

[PATCH] D80426: [analyzer] SATestUpdateDiffs.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5a9aff12ff3b: [analyzer] SATestUpdateDiffs.py: Refactor and 
add type annotations (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80426/new/

https://reviews.llvm.org/D80426

Files:
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -3,67 +3,67 @@
 """
 Update reference results for static analyzer.
 """
-from __future__ import absolute_import, division, print_function
-
 import SATestBuild
 
-from subprocess import check_call
 import os
+import shutil
 import sys
 
+from subprocess import check_call
+
 Verbose = 0
 
 
-def runCmd(Command, **kwargs):
-if Verbose:
-print("Executing %s" % Command)
-check_call(Command, shell=True, **kwargs)
+def update_reference_results(project_name: str, build_mode: int):
+project_info = SATestBuild.ProjectInfo(project_name, build_mode)
+tester = SATestBuild.ProjectTester(project_info)
+project_dir = tester.get_project_dir()
 
+tester.is_reference_build = True
+ref_results_path = os.path.join(project_dir, tester.get_output_dir())
 
-def updateReferenceResults(ProjName, ProjBuildMode):
-ProjInfo = SATestBuild.ProjectInfo(ProjName, ProjBuildMode)
-ProjTester = SATestBuild.ProjectTester(ProjInfo)
-ProjDir = ProjTester.get_project_dir()
+tester.is_reference_build = False
+created_results_path = os.path.join(project_dir, tester.get_output_dir())
 
-ProjTester.is_reference_build = True
-RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+if not os.path.exists(created_results_path):
+print("New results not found, was SATestBuild.py previously run?",
+  file=sys.stderr)
+sys.exit(1)
 
-ProjTester.is_reference_build = False
-CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir())
+build_log_path = SATestBuild.get_build_log_path(ref_results_path)
+build_log_dir = os.path.dirname(os.path.abspath(build_log_path))
 
-if not os.path.exists(CreatedResultsPath):
-print("New results not found, was SATestBuild.py "
-  "previously run?", file=sys.stderr)
-sys.exit(1)
+os.makedirs(build_log_dir)
+
+with open(build_log_path, "w+") as build_log_file:
+def run_cmd(command: str):
+if Verbose:
+print(f"Executing {command}")
+check_call(command, shell=True, stdout=build_log_file)
 
-BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath)
-Dirname = os.path.dirname(os.path.abspath(BuildLogPath))
-runCmd("mkdir -p '%s'" % Dirname)
-with open(BuildLogPath, "w+") as PBuildLogFile:
 # Remove reference results: in git, and then again for a good measure
 # with rm, as git might not remove things fully if there are empty
 # directories involved.
-runCmd('git rm -r -q "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
-runCmd('rm -rf "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
+run_cmd(f"git rm -r -q '{ref_results_path}'")
+shutil.rmtree(ref_results_path)
 
 # Replace reference results with a freshly computed once.
-runCmd('cp -r "%s" "%s"' % (CreatedResultsPath, RefResultsPath,),
-   stdout=PBuildLogFile)
+shutil.copytree(created_results_path, ref_results_path, symlinks=True)
 
 # Run cleanup script.
-SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile)
+SATestBuild.run_cleanup_script(project_dir, build_log_file)
 
 SATestBuild.normalize_reference_results(
-ProjDir, RefResultsPath, ProjBuildMode)
+project_dir, ref_results_path, build_mode)
 
 # Clean up the generated difference results.
-SATestBuild.cleanup_reference_results(RefResultsPath)
+SATestBuild.cleanup_reference_results(ref_results_path)
 
-runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile)
+run_cmd(f"git add '{ref_results_path}'")
 
 
 def main(argv):
-if len(argv) == 2 and argv[1] in ('-h', '--help'):
+if len(argv) == 2 and argv[1] in ("-h", "--help"):
 print("Update static analyzer reference results based "
   "\non the previous run of SATestBuild.py.\n"
   "\nN.B.: Assumes that SATestBuild.py was just run",
@@ -71,8 +71,8 @@
 sys.exit(1)
 
 with open(SATestBuild.get_project_map_path(), "r") as f:
-for ProjName, ProjBuildMode in SATestBuild.get_projects(f):
-updateReferenceResults(ProjName, int(ProjBuildMode))
+for project_name, build_mode in SATestBuild.get_projects(f):
+update_reference_results(project_name, int(build_mode))
 
 
 if __nam

[PATCH] D80424: [analyzer] SATestUtils.py: Refactor and add type annotations

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG7cebfa4e0692: [analyzer] SATestUtils.py: Refactor and add 
type annotations (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80424/new/

https://reviews.llvm.org/D80424

Files:
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUtils.py

Index: clang/utils/analyzer/SATestUtils.py
===
--- clang/utils/analyzer/SATestUtils.py
+++ 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,44 @@
 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: str) -> bool:
+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(f"  Executing: {script_path}\n")
+
+check_call(f"chmod +x '{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(f"'{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(f"Error: Running {script_path} failed. "
+  f"See {build_log_file.name} for details.\n")
 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("#")
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ 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.std

[PATCH] D80427: [analyzer] SumTimerInfo.py: Partially modernize

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG475d12028370: [analyzer] SumTimerInfo.py: Partially 
modernize (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80427/new/

https://reviews.llvm.org/D80427

Files:
  clang/utils/analyzer/SumTimerInfo.py

Index: clang/utils/analyzer/SumTimerInfo.py
===
--- clang/utils/analyzer/SumTimerInfo.py
+++ clang/utils/analyzer/SumTimerInfo.py
@@ -6,8 +6,6 @@
 Statistics are enabled by passing '-internal-stats' option to scan-build
 (or '-analyzer-stats' to the analyzer).
 """
-from __future__ import absolute_import, division, print_function
-
 import sys
 
 if __name__ == '__main__':
@@ -17,64 +15,65 @@
 sys.exit(-1)
 
 f = open(sys.argv[1], 'r')
-Time = 0.0
-TotalTime = 0.0
-MaxTime = 0.0
-Warnings = 0
-Count = 0
-FunctionsAnalyzed = 0
-ReachableBlocks = 0
-ReachedMaxSteps = 0
-NumSteps = 0
-NumInlinedCallSites = 0
-NumBifurcatedCallSites = 0
-MaxCFGSize = 0
+time = 0.0
+total_time = 0.0
+max_time = 0.0
+warnings = 0
+count = 0
+functions_analyzed = 0
+reachable_blocks = 0
+reached_max_steps = 0
+num_steps = 0
+num_inlined_call_sites = 0
+num_bifurcated_call_sites = 0
+max_cfg_size = 0
+
 for line in f:
-if ("Analyzer Total Time" in line):
+if "Analyzer total time" in line:
 s = line.split()
-Time = Time + float(s[6])
-Count = Count + 1
-if (float(s[6]) > MaxTime):
-MaxTime = float(s[6])
-if ("warning generated." in line) or ("warnings generated" in line):
+time = time + float(s[6])
+count = count + 1
+if float(s[6]) > max_time:
+max_time = float(s[6])
+if "warning generated." in line or "warnings generated" in line:
 s = line.split()
-Warnings = Warnings + int(s[0])
+warnings = warnings + int(s[0])
 if "The # of functions analysed (as top level)" in line:
 s = line.split()
-FunctionsAnalyzed = FunctionsAnalyzed + int(s[0])
+functions_analyzed = functions_analyzed + int(s[0])
 if "The % of reachable basic blocks" in line:
 s = line.split()
-ReachableBlocks = ReachableBlocks + int(s[0])
+reachable_blocks = reachable_blocks + int(s[0])
 if "The # of times we reached the max number of steps" in line:
 s = line.split()
-ReachedMaxSteps = ReachedMaxSteps + int(s[0])
+reached_max_steps = reached_max_steps + int(s[0])
 if "The maximum number of basic blocks in a function" in line:
 s = line.split()
-if MaxCFGSize < int(s[0]):
-MaxCFGSize = int(s[0])
+if max_cfg_size < int(s[0]):
+max_cfg_size = int(s[0])
 if "The # of steps executed" in line:
 s = line.split()
-NumSteps = NumSteps + int(s[0])
+num_steps = num_steps + int(s[0])
 if "The # of times we inlined a call" in line:
 s = line.split()
-NumInlinedCallSites = NumInlinedCallSites + int(s[0])
+num_inlined_call_sites = num_inlined_call_sites + int(s[0])
 if "The # of times we split the path due \
 to imprecise dynamic dispatch info" in line:
 s = line.split()
-NumBifurcatedCallSites = NumBifurcatedCallSites + int(s[0])
+num_bifurcated_call_sites = num_bifurcated_call_sites + int(s[0])
 if ")  Total" in line:
 s = line.split()
-TotalTime = TotalTime + float(s[6])
+total_time = total_time + float(s[6])
 
-print("TU Count %d" % (Count))
-print("Time %f" % (Time))
-print("Warnings %d" % (Warnings))
-print("Functions Analyzed %d" % (FunctionsAnalyzed))
-print("Reachable Blocks %d" % (ReachableBlocks))
-print("Reached Max Steps %d" % (ReachedMaxSteps))
-print("Number of Steps %d" % (NumSteps))
-print("Number of Inlined calls %d (bifurcated %d)" % (
-NumInlinedCallSites, NumBifurcatedCallSites))
-print("MaxTime %f" % (MaxTime))
-print("TotalTime %f" % (TotalTime))
-print("Max CFG Size %d" % (MaxCFGSize))
+print(f"TU count {count}")
+print(f"Time {time}")
+print(f"Warnings {warnings}")
+print(f"Functions analyzed {functions_analyzed}")
+print(f"Reachable blocks {reachable_blocks}")
+print(f"Reached max steps {reached_max_steps}")
+print(f"Number of steps {num_steps}")
+print(f"Number of inlined calls {num_inlined_call_sites} "
+  f"(bifurcated {num_bifurcated_call_sites})")
+print(f"Max time {max_time}")
+print(f"Total time {total_time}")
+print(f"Max CFG Size {max_cfg_s

[PATCH] D80443: [analyzer] SATestBuild.py: Fix hang when one of the tasks fails

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added a reviewer: NoQ.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Tasks can crash with many different exceptions including SystemExit.
Bare except still causes a warning, so let's use BaseException instead.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80443

Files:
  clang/utils/analyzer/SATestBuild.py


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -633,7 +633,7 @@
 
 self.tasks_queue.task_done()
 
-except CalledProcessError:
+except BaseException:
 self.failure_flag.set()
 raise
 


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -633,7 +633,7 @@
 
 self.tasks_queue.task_done()
 
-except CalledProcessError:
+except BaseException:
 self.failure_flag.set()
 raise
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80443: [analyzer] SATestBuild.py: Fix hang when one of the tasks fails

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa5b2503a8ab4: [analyzer] SATestBuild.py: Fix hang when one 
of the tasks fails (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80443/new/

https://reviews.llvm.org/D80443

Files:
  clang/utils/analyzer/SATestBuild.py


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -633,7 +633,7 @@
 
 self.tasks_queue.task_done()
 
-except CalledProcessError:
+except BaseException:
 self.failure_flag.set()
 raise
 


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -633,7 +633,7 @@
 
 self.tasks_queue.task_done()
 
-except CalledProcessError:
+except BaseException:
 self.failure_flag.set()
 raise
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D76510: [analyzer] Change the default output type to PD_TEXT_MINIMAL in the frontend, error if an output loc is missing for PathDiagConsumers that need it

2020-05-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Hi @Szelethus , it looks like I am a bearer of bad news again :(
This patch seems to crash `scan-build` on pretty much every project that I 
tested.

One way to reproduce:

  git clone https://github.com/postgres/postgres.git
  cd postgres/
  scan-build -plist-html -o ./out ./configure 

Output:

  scan-build: Using 
'/Users/vsavchenko/source/llvm-project/build/Release/bin/clang-11' for static 
analysis
  checking build system type... x86_64-apple-darwin19.4.0
  checking host system type... x86_64-apple-darwin19.4.0
  checking which template to use... darwin
  checking whether NLS is wanted... no
  checking for default port number... 5432
  checking for block size... 8kB
  checking for segment size... 1GB
  checking for WAL block size... 8kB
  checking whether the C compiler works... no
  configure: error: in `/Users/vsavchenko/source/postgres':
  configure: error: C compiler cannot create executables
  See `config.log' for more details
  scan-build: Analysis run complete.
  scan-build: Analysis results (plist files) deposited in 
'/Users/vsavchenko/source/postgres/out/2020-05-22-194935-27687-1'
  scan-build: Removing directory 
'/Users/vsavchenko/source/postgres/out/2020-05-22-194935-27687-1' because it 
contains no reports.
  scan-build: No bugs found.

config.log will contain something similar to this:

  configure:3987: checking whether the C compiler works
  configure:4009: 
/Users/vsavchenko/source/llvm-project/build/Release/bin/../libexec/ccc-analyzer 
   conftest.c  >&5
  error: analyzer output type 'html' requires an output directory to be 
specified with -o 
  1 error generated.
  Use of uninitialized value $HtmlDir in concatenation (.) or string at 
/Users/vsavchenko/source/llvm-project/build/Release/bin/../libexec/ccc-analyzer 
line 149, <$fh> line 2.
  mkdir /failures: Read-only file system at 
/Users/vsavchenko/source/llvm-project/build/Release/bin/../libexec/ccc-analyzer 
line 150.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D76510/new/

https://reviews.llvm.org/D76510



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80517: [analyzer] CmpRuns.py: Refactor and add type annotations

2020-05-25 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80517

Files:
  clang/utils/analyzer/CmpRuns.py

Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -17,26 +17,35 @@
 # Load the results of both runs, to obtain lists of the corresponding
 # AnalysisDiagnostic objects.
 #
-resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
-resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
+resultsA = load_results_from_single_run(singleRunInfoA, delete_empty)
+resultsB = load_results_from_single_run(singleRunInfoB, delete_empty)
 
 # Generate a relation from diagnostics in run A to diagnostics in run B
 # to obtain a list of triples (a, b, confidence).
-diff = compareResults(resultsA, resultsB)
+diff = compare_results(resultsA, resultsB)
 
 """
-from __future__ import division, print_function
-
-from collections import defaultdict
-
-from math import log
-from optparse import OptionParser
+import argparse
 import json
 import os
 import plistlib
 import re
 import sys
 
+from math import log
+from collections import defaultdict
+from copy import copy
+from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
+Tuple, Union)
+
+
+Number = Union[int, float]
+Stats = Dict[str, Dict[str, Number]]
+Plist = Dict[str, Any]
+JSON = Dict[str, Any]
+# Type for generics
+T = TypeVar('T')
+
 STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL)
 
 
@@ -56,118 +65,126 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path, root="", verboseLog=None):
+def __init__(self, path: str, root: str = "", verbose_log=None):
 self.path = path
 self.root = root.rstrip("/\\")
-self.verboseLog = verboseLog
+self.verbose_log = verbose_log
 
 
 class AnalysisDiagnostic:
-def __init__(self, data, report, htmlReport):
+def __init__(self, data: Plist, report: "AnalysisReport",
+ html_report: Optional[str]):
 self._data = data
 self._loc = self._data['location']
 self._report = report
-self._htmlReport = htmlReport
-self._reportSize = len(self._data['path'])
+self._html_report = html_report
+self._report_size = len(self._data['path'])
 
-def getFileName(self):
+def get_file_name(self) -> str:
 root = self._report.run.root
-fileName = self._report.files[self._loc['file']]
-if fileName.startswith(root) and len(root) > 0:
-return fileName[len(root) + 1:]
-return fileName
+file_name = self._report.files[self._loc['file']]
+
+if file_name.startswith(root) and len(root) > 0:
+return file_name[len(root) + 1:]
 
-def getRootFileName(self):
+return file_name
+
+def get_root_file_name(self) -> str:
 path = self._data['path']
+
 if not path:
-return self.getFileName()
+return self.get_file_name()
+
 p = path[0]
 if 'location' in p:
-fIdx = p['location']['file']
+file_index = p['location']['file']
 else:  # control edge
-fIdx = path[0]['edges'][0]['start'][0]['file']
-out = self._report.files[fIdx]
+file_index = path[0]['edges'][0]['start'][0]['file']
+
+out = self._report.files[file_index]
 root = self._report.run.root
+
 if out.startswith(root):
 return out[len(root):]
+
 return out
 
-def getLine(self):
+def get_line(self) -> int:
 return self._loc['line']
 
-def getColumn(self):
+def get_column(self) -> int:
 return self._loc['col']
 
-def getPathLength(self):
-return self._reportSize
+def get_path_length(self) -> int:
+return self._report_size
 
-def getCategory(self):
+def get_category(self) -> str:
 return self._data['category']
 
-def getDescription(self):
+def get_description(self) -> str:
 return self._data['description']
 
-def getIssueIdentifier(self):
-id = self.getFileName() + "+"
-if 'issue_context' in self._data:
-id += self._data['issue_context'] + "+"
-if 'issue_hash_content_of_line_in_context' in self._data:
-id += str(self._data['issue_hash_content_of_line_in_context'])
+def get_issue_identifier(self) -> str:
+id = self.get_file_name() + "+"
+
+if "issue_context" in self._data:
+

[PATCH] D80517: [analyzer] CmpRuns.py: Refactor and add type annotations

2020-05-25 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266015.
vsavchenko added a comment.

Fix a couple more names


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80517/new/

https://reviews.llvm.org/D80517

Files:
  clang/utils/analyzer/CmpRuns.py

Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -17,26 +17,35 @@
 # Load the results of both runs, to obtain lists of the corresponding
 # AnalysisDiagnostic objects.
 #
-resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
-resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
+resultsA = load_results_from_single_run(singleRunInfoA, delete_empty)
+resultsB = load_results_from_single_run(singleRunInfoB, delete_empty)
 
 # Generate a relation from diagnostics in run A to diagnostics in run B
 # to obtain a list of triples (a, b, confidence).
-diff = compareResults(resultsA, resultsB)
+diff = compare_results(resultsA, resultsB)
 
 """
-from __future__ import division, print_function
-
-from collections import defaultdict
-
-from math import log
-from optparse import OptionParser
+import argparse
 import json
 import os
 import plistlib
 import re
 import sys
 
+from math import log
+from collections import defaultdict
+from copy import copy
+from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
+Tuple, Union)
+
+
+Number = Union[int, float]
+Stats = Dict[str, Dict[str, Number]]
+Plist = Dict[str, Any]
+JSON = Dict[str, Any]
+# Type for generics
+T = TypeVar('T')
+
 STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL)
 
 
@@ -56,118 +65,127 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path, root="", verboseLog=None):
+def __init__(self, path: str, root: str = "", verbose_log=None):
 self.path = path
 self.root = root.rstrip("/\\")
-self.verboseLog = verboseLog
+self.verbose_log = verbose_log
 
 
 class AnalysisDiagnostic:
-def __init__(self, data, report, htmlReport):
+def __init__(self, data: Plist, report: "AnalysisReport",
+ html_report: Optional[str]):
 self._data = data
 self._loc = self._data['location']
 self._report = report
-self._htmlReport = htmlReport
-self._reportSize = len(self._data['path'])
+self._html_report = html_report
+self._report_size = len(self._data['path'])
 
-def getFileName(self):
+def get_file_name(self) -> str:
 root = self._report.run.root
-fileName = self._report.files[self._loc['file']]
-if fileName.startswith(root) and len(root) > 0:
-return fileName[len(root) + 1:]
-return fileName
+file_name = self._report.files[self._loc['file']]
+
+if file_name.startswith(root) and len(root) > 0:
+return file_name[len(root) + 1:]
 
-def getRootFileName(self):
+return file_name
+
+def get_root_file_name(self) -> str:
 path = self._data['path']
+
 if not path:
-return self.getFileName()
+return self.get_file_name()
+
 p = path[0]
 if 'location' in p:
-fIdx = p['location']['file']
+file_index = p['location']['file']
 else:  # control edge
-fIdx = path[0]['edges'][0]['start'][0]['file']
-out = self._report.files[fIdx]
+file_index = path[0]['edges'][0]['start'][0]['file']
+
+out = self._report.files[file_index]
 root = self._report.run.root
+
 if out.startswith(root):
 return out[len(root):]
+
 return out
 
-def getLine(self):
+def get_line(self) -> int:
 return self._loc['line']
 
-def getColumn(self):
+def get_column(self) -> int:
 return self._loc['col']
 
-def getPathLength(self):
-return self._reportSize
+def get_path_length(self) -> int:
+return self._report_size
 
-def getCategory(self):
+def get_category(self) -> str:
 return self._data['category']
 
-def getDescription(self):
+def get_description(self) -> str:
 return self._data['description']
 
-def getIssueIdentifier(self):
-id = self.getFileName() + "+"
-if 'issue_context' in self._data:
-id += self._data['issue_context'] + "+"
-if 'issue_hash_content_of_line_in_context' in self._data:
-id += str(self._data['issue_hash_content_of_line_in_context'])
+def get_issue_identifier(self) -> str:
+id = self.get_file_name() + "+"
+
+if "issue_context" in self._data:
+id += self._data["issue_context"] + "+"
+
+if "issue_hash_content_of_line_in_context

[PATCH] D80517: [analyzer] CmpRuns.py: Refactor and add type annotations

2020-05-25 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266014.
vsavchenko added a comment.

Replace deprecated method call


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80517/new/

https://reviews.llvm.org/D80517

Files:
  clang/utils/analyzer/CmpRuns.py

Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -17,26 +17,35 @@
 # Load the results of both runs, to obtain lists of the corresponding
 # AnalysisDiagnostic objects.
 #
-resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
-resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
+resultsA = load_results_from_single_run(singleRunInfoA, delete_empty)
+resultsB = load_results_from_single_run(singleRunInfoB, delete_empty)
 
 # Generate a relation from diagnostics in run A to diagnostics in run B
 # to obtain a list of triples (a, b, confidence).
-diff = compareResults(resultsA, resultsB)
+diff = compare_results(resultsA, resultsB)
 
 """
-from __future__ import division, print_function
-
-from collections import defaultdict
-
-from math import log
-from optparse import OptionParser
+import argparse
 import json
 import os
 import plistlib
 import re
 import sys
 
+from math import log
+from collections import defaultdict
+from copy import copy
+from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
+Tuple, Union)
+
+
+Number = Union[int, float]
+Stats = Dict[str, Dict[str, Number]]
+Plist = Dict[str, Any]
+JSON = Dict[str, Any]
+# Type for generics
+T = TypeVar('T')
+
 STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL)
 
 
@@ -56,118 +65,127 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path, root="", verboseLog=None):
+def __init__(self, path: str, root: str = "", verbose_log=None):
 self.path = path
 self.root = root.rstrip("/\\")
-self.verboseLog = verboseLog
+self.verbose_log = verbose_log
 
 
 class AnalysisDiagnostic:
-def __init__(self, data, report, htmlReport):
+def __init__(self, data: Plist, report: "AnalysisReport",
+ html_report: Optional[str]):
 self._data = data
 self._loc = self._data['location']
 self._report = report
-self._htmlReport = htmlReport
-self._reportSize = len(self._data['path'])
+self._html_report = html_report
+self._report_size = len(self._data['path'])
 
-def getFileName(self):
+def get_file_name(self) -> str:
 root = self._report.run.root
-fileName = self._report.files[self._loc['file']]
-if fileName.startswith(root) and len(root) > 0:
-return fileName[len(root) + 1:]
-return fileName
+file_name = self._report.files[self._loc['file']]
+
+if file_name.startswith(root) and len(root) > 0:
+return file_name[len(root) + 1:]
 
-def getRootFileName(self):
+return file_name
+
+def get_root_file_name(self) -> str:
 path = self._data['path']
+
 if not path:
-return self.getFileName()
+return self.get_file_name()
+
 p = path[0]
 if 'location' in p:
-fIdx = p['location']['file']
+file_index = p['location']['file']
 else:  # control edge
-fIdx = path[0]['edges'][0]['start'][0]['file']
-out = self._report.files[fIdx]
+file_index = path[0]['edges'][0]['start'][0]['file']
+
+out = self._report.files[file_index]
 root = self._report.run.root
+
 if out.startswith(root):
 return out[len(root):]
+
 return out
 
-def getLine(self):
+def get_line(self) -> int:
 return self._loc['line']
 
-def getColumn(self):
+def get_column(self) -> int:
 return self._loc['col']
 
-def getPathLength(self):
-return self._reportSize
+def get_path_length(self) -> int:
+return self._report_size
 
-def getCategory(self):
+def get_category(self) -> str:
 return self._data['category']
 
-def getDescription(self):
+def get_description(self) -> str:
 return self._data['description']
 
-def getIssueIdentifier(self):
-id = self.getFileName() + "+"
-if 'issue_context' in self._data:
-id += self._data['issue_context'] + "+"
-if 'issue_hash_content_of_line_in_context' in self._data:
-id += str(self._data['issue_hash_content_of_line_in_context'])
+def get_issue_identifier(self) -> str:
+id = self.get_file_name() + "+"
+
+if "issue_context" in self._data:
+id += self._data["issue_context"] + "+"
+
+if "issue_hash_content_of_line_in_

[PATCH] D80517: [analyzer] CmpRuns.py: Refactor and add type annotations

2020-05-25 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266016.
vsavchenko added a comment.

Fix SATestBuild


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80517/new/

https://reviews.llvm.org/D80517

Files:
  clang/utils/analyzer/CmpRuns.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -754,9 +754,9 @@
 ["--rootA", "", "--rootB", patched_source])
 # Scan the results, delete empty plist files.
 num_diffs, reports_in_ref, reports_in_new = \
-CmpRuns.dumpScanBuildResultsDiff(ref_dir, new_dir, opts,
- deleteEmpty=False,
- Stdout=LOCAL.stdout)
+CmpRuns.dump_scan_build_results_diff(ref_dir, new_dir, opts,
+ delete_empty=False,
+ out=LOCAL.stdout)
 
 if num_diffs > 0:
 stdout(f"Warning: {num_diffs} differences in diagnostics.\n")
@@ -834,7 +834,8 @@
 plist = os.path.join(output_dir, plist)
 
 try:
-data = plistlib.readPlist(plist)
+with open(plist, "rb") as plist_file:
+data = plistlib.load(plist_file)
 # Delete empty reports.
 if not data['files']:
 os.remove(plist)
Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -17,26 +17,35 @@
 # Load the results of both runs, to obtain lists of the corresponding
 # AnalysisDiagnostic objects.
 #
-resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
-resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
+resultsA = load_results_from_single_run(singleRunInfoA, delete_empty)
+resultsB = load_results_from_single_run(singleRunInfoB, delete_empty)
 
 # Generate a relation from diagnostics in run A to diagnostics in run B
 # to obtain a list of triples (a, b, confidence).
-diff = compareResults(resultsA, resultsB)
+diff = compare_results(resultsA, resultsB)
 
 """
-from __future__ import division, print_function
-
-from collections import defaultdict
-
-from math import log
-from optparse import OptionParser
+import argparse
 import json
 import os
 import plistlib
 import re
 import sys
 
+from math import log
+from collections import defaultdict
+from copy import copy
+from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
+Tuple, Union)
+
+
+Number = Union[int, float]
+Stats = Dict[str, Dict[str, Number]]
+Plist = Dict[str, Any]
+JSON = Dict[str, Any]
+# Type for generics
+T = TypeVar('T')
+
 STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL)
 
 
@@ -56,118 +65,127 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path, root="", verboseLog=None):
+def __init__(self, path: str, root: str = "", verbose_log=None):
 self.path = path
 self.root = root.rstrip("/\\")
-self.verboseLog = verboseLog
+self.verbose_log = verbose_log
 
 
 class AnalysisDiagnostic:
-def __init__(self, data, report, htmlReport):
+def __init__(self, data: Plist, report: "AnalysisReport",
+ html_report: Optional[str]):
 self._data = data
 self._loc = self._data['location']
 self._report = report
-self._htmlReport = htmlReport
-self._reportSize = len(self._data['path'])
+self._html_report = html_report
+self._report_size = len(self._data['path'])
 
-def getFileName(self):
+def get_file_name(self) -> str:
 root = self._report.run.root
-fileName = self._report.files[self._loc['file']]
-if fileName.startswith(root) and len(root) > 0:
-return fileName[len(root) + 1:]
-return fileName
+file_name = self._report.files[self._loc['file']]
+
+if file_name.startswith(root) and len(root) > 0:
+return file_name[len(root) + 1:]
 
-def getRootFileName(self):
+return file_name
+
+def get_root_file_name(self) -> str:
 path = self._data['path']
+
 if not path:
-return self.getFileName()
+return self.get_file_name()
+
 p = path[0]
 if 'location' in p:
-fIdx = p['location']['file']
+file_index = p['location']['file']
 else:  # control edge
-fIdx = path[0]['edges'][0]['start'][0]['file']
-out = self._report.files[fIdx]
+file_index = path[0]['edges'][0]['start'][0]['file']
+
+   

[PATCH] D80626: [analyzer] SATestBuild.py: Make verbosity level a cmd option

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80626

Files:
  clang/utils/analyzer/SATestBuild.py


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -389,7 +389,7 @@
 start_time = time.time()
 
 project_dir = self.get_project_dir()
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Build directory: {project_dir}.\n")
 
 # Set the build results directory.
@@ -431,7 +431,7 @@
 
 # Clean up scan build results.
 if os.path.exists(output_dir):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing old results: {output_dir}\n")
 
 shutil.rmtree(output_dir)
@@ -517,7 +517,7 @@
 
 command_to_run = command_prefix + command
 
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Executing: {command_to_run}\n")
 
 check_call(command_to_run, cwd=cwd,
@@ -575,7 +575,7 @@
 log_path = os.path.join(fail_path, file_name + ".stderr.txt")
 with open(log_path, "w+") as log_file:
 try:
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Executing: {command}\n")
 
 check_call(command, cwd=directory, stderr=log_file,
@@ -744,7 +744,7 @@
 for ref_dir, new_dir in zip(ref_list, new_list):
 assert(ref_dir != new_dir)
 
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Comparing Results: {ref_dir} {new_dir}\n")
 
 patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
@@ -818,7 +818,7 @@
 
 # Clean up the log file.
 if os.path.exists(build_log_path):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing log file: {build_log_path}\n")
 
 os.remove(build_log_path)
@@ -888,28 +888,32 @@
 
 if __name__ == "__main__":
 # Parse command line arguments.
-Parser = argparse.ArgumentParser(
+parser = argparse.ArgumentParser(
 description="Test the Clang Static Analyzer.")
 
-Parser.add_argument("--strictness", dest="strictness", type=int, default=0,
+parser.add_argument("--strictness", dest="strictness", type=int, default=0,
 help="0 to fail on runtime errors, 1 to fail when the "
 "number of found bugs are different from the "
 "reference, 2 to fail on any difference from the "
 "reference. Default is 0.")
-Parser.add_argument("-r", dest="regenerate", action="store_true",
+parser.add_argument("-r", dest="regenerate", action="store_true",
 default=False, help="Regenerate reference output.")
-Parser.add_argument("--override-compiler", action="store_true",
+parser.add_argument("--override-compiler", action="store_true",
 default=False, help="Call scan-build with "
 "--override-compiler option.")
-Parser.add_argument("-j", "--jobs", dest="jobs", type=int,
+parser.add_argument("-j", "--jobs", dest="jobs", type=int,
 default=0,
 help="Number of projects to test concurrently")
-Parser.add_argument("--extra-analyzer-config",
+parser.add_argument("--extra-analyzer-config",
 dest="extra_analyzer_config", type=str,
 default="",
 help="Arguments passed to to -analyzer-config")
+parser.add_argument("-v", "--verbose", action="count", default=0)
 
-args = Parser.parse_args()
+args = parser.parse_args()
+
+global VERBOSE
+VERBOSE = args.verbose
 
 tester = RegressionTester(args.jobs, args.override_compiler,
   args.extra_analyzer_config, args.regenerate,


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -389,7 +389,7 @@
 start_time = time.time()
 
 project_dir = self.get_project_dir()
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Build directory: {project_dir}.\n")
 
 # Set the build results directory.
@@ -431,7 +431,7 @@
 
 # Clean up scan build results.
 if os.path.exists(output_dir):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing old results: {output_dir}\n")
 

[PATCH] D80626: [analyzer] SATestBuild.py: Make verbosity level a cmd option

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266518.
vsavchenko added a comment.

Fix global issue


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80626/new/

https://reviews.llvm.org/D80626

Files:
  clang/utils/analyzer/SATestBuild.py


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -389,7 +389,7 @@
 start_time = time.time()
 
 project_dir = self.get_project_dir()
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Build directory: {project_dir}.\n")
 
 # Set the build results directory.
@@ -431,7 +431,7 @@
 
 # Clean up scan build results.
 if os.path.exists(output_dir):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing old results: {output_dir}\n")
 
 shutil.rmtree(output_dir)
@@ -517,7 +517,7 @@
 
 command_to_run = command_prefix + command
 
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Executing: {command_to_run}\n")
 
 check_call(command_to_run, cwd=cwd,
@@ -575,7 +575,7 @@
 log_path = os.path.join(fail_path, file_name + ".stderr.txt")
 with open(log_path, "w+") as log_file:
 try:
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Executing: {command}\n")
 
 check_call(command, cwd=directory, stderr=log_file,
@@ -744,7 +744,7 @@
 for ref_dir, new_dir in zip(ref_list, new_list):
 assert(ref_dir != new_dir)
 
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Comparing Results: {ref_dir} {new_dir}\n")
 
 patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
@@ -818,7 +818,7 @@
 
 # Clean up the log file.
 if os.path.exists(build_log_path):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing log file: {build_log_path}\n")
 
 os.remove(build_log_path)
@@ -888,29 +888,31 @@
 
 if __name__ == "__main__":
 # Parse command line arguments.
-Parser = argparse.ArgumentParser(
+parser = argparse.ArgumentParser(
 description="Test the Clang Static Analyzer.")
 
-Parser.add_argument("--strictness", dest="strictness", type=int, default=0,
+parser.add_argument("--strictness", dest="strictness", type=int, default=0,
 help="0 to fail on runtime errors, 1 to fail when the "
 "number of found bugs are different from the "
 "reference, 2 to fail on any difference from the "
 "reference. Default is 0.")
-Parser.add_argument("-r", dest="regenerate", action="store_true",
+parser.add_argument("-r", dest="regenerate", action="store_true",
 default=False, help="Regenerate reference output.")
-Parser.add_argument("--override-compiler", action="store_true",
+parser.add_argument("--override-compiler", action="store_true",
 default=False, help="Call scan-build with "
 "--override-compiler option.")
-Parser.add_argument("-j", "--jobs", dest="jobs", type=int,
+parser.add_argument("-j", "--jobs", dest="jobs", type=int,
 default=0,
 help="Number of projects to test concurrently")
-Parser.add_argument("--extra-analyzer-config",
+parser.add_argument("--extra-analyzer-config",
 dest="extra_analyzer_config", type=str,
 default="",
 help="Arguments passed to to -analyzer-config")
+parser.add_argument("-v", "--verbose", action="count", default=0)
 
-args = Parser.parse_args()
+args = parser.parse_args()
 
+VERBOSE = args.verbose
 tester = RegressionTester(args.jobs, args.override_compiler,
   args.extra_analyzer_config, args.regenerate,
   args.strictness)


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -389,7 +389,7 @@
 start_time = time.time()
 
 project_dir = self.get_project_dir()
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Build directory: {project_dir}.\n")
 
 # Set the build results directory.
@@ -431,7 +431,7 @@
 
 # Clean up scan build results.
 if os.path.exists(output_dir):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing old results: {output_dir}\n")
 
 shutil.rmtree(output_dir)
@@ -517,7 +517,7 @@
 
 comman

[PATCH] D80517: [analyzer] CmpRuns.py: Refactor and add type annotations

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266520.
vsavchenko added a comment.

Fix SATestBuild call to CmpRuns


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80517/new/

https://reviews.llvm.org/D80517

Files:
  clang/utils/analyzer/CmpRuns.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -751,12 +751,12 @@
 
 # TODO: get rid of option parser invocation here
 opts, args = CmpRuns.generate_option_parser().parse_args(
-["--rootA", "", "--rootB", patched_source])
+["--root-old", "", "--root-new", patched_source])
 # Scan the results, delete empty plist files.
 num_diffs, reports_in_ref, reports_in_new = \
-CmpRuns.dumpScanBuildResultsDiff(ref_dir, new_dir, opts,
- deleteEmpty=False,
- Stdout=LOCAL.stdout)
+CmpRuns.dump_scan_build_results_diff(ref_dir, new_dir, opts,
+ delete_empty=False,
+ out=LOCAL.stdout)
 
 if num_diffs > 0:
 stdout(f"Warning: {num_diffs} differences in diagnostics.\n")
@@ -834,7 +834,8 @@
 plist = os.path.join(output_dir, plist)
 
 try:
-data = plistlib.readPlist(plist)
+with open(plist, "rb") as plist_file:
+data = plistlib.load(plist_file)
 # Delete empty reports.
 if not data['files']:
 os.remove(plist)
Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -17,26 +17,35 @@
 # Load the results of both runs, to obtain lists of the corresponding
 # AnalysisDiagnostic objects.
 #
-resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
-resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
+resultsA = load_results_from_single_run(singleRunInfoA, delete_empty)
+resultsB = load_results_from_single_run(singleRunInfoB, delete_empty)
 
 # Generate a relation from diagnostics in run A to diagnostics in run B
 # to obtain a list of triples (a, b, confidence).
-diff = compareResults(resultsA, resultsB)
+diff = compare_results(resultsA, resultsB)
 
 """
-from __future__ import division, print_function
-
-from collections import defaultdict
-
-from math import log
-from optparse import OptionParser
+import argparse
 import json
 import os
 import plistlib
 import re
 import sys
 
+from math import log
+from collections import defaultdict
+from copy import copy
+from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
+Tuple, Union)
+
+
+Number = Union[int, float]
+Stats = Dict[str, Dict[str, Number]]
+Plist = Dict[str, Any]
+JSON = Dict[str, Any]
+# Type for generics
+T = TypeVar('T')
+
 STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL)
 
 
@@ -56,118 +65,127 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path, root="", verboseLog=None):
+def __init__(self, path: str, root: str = "", verbose_log=None):
 self.path = path
 self.root = root.rstrip("/\\")
-self.verboseLog = verboseLog
+self.verbose_log = verbose_log
 
 
 class AnalysisDiagnostic:
-def __init__(self, data, report, htmlReport):
+def __init__(self, data: Plist, report: "AnalysisReport",
+ html_report: Optional[str]):
 self._data = data
 self._loc = self._data['location']
 self._report = report
-self._htmlReport = htmlReport
-self._reportSize = len(self._data['path'])
+self._html_report = html_report
+self._report_size = len(self._data['path'])
 
-def getFileName(self):
+def get_file_name(self) -> str:
 root = self._report.run.root
-fileName = self._report.files[self._loc['file']]
-if fileName.startswith(root) and len(root) > 0:
-return fileName[len(root) + 1:]
-return fileName
+file_name = self._report.files[self._loc['file']]
+
+if file_name.startswith(root) and len(root) > 0:
+return file_name[len(root) + 1:]
 
-def getRootFileName(self):
+return file_name
+
+def get_root_file_name(self) -> str:
 path = self._data['path']
+
 if not path:
-return self.getFileName()
+return self.get_file_name()
+
 p = path[0]
 if 'location' in p:
-fIdx = p['location']['file']
+file_index = p['location']['file']

[PATCH] D80517: [analyzer] CmpRuns.py: Refactor and add type annotations

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266521.
vsavchenko added a comment.

Fix SATestBuild call to CmpRuns


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80517/new/

https://reviews.llvm.org/D80517

Files:
  clang/utils/analyzer/CmpRuns.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -750,13 +750,13 @@
 patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
 
 # TODO: get rid of option parser invocation here
-opts, args = CmpRuns.generate_option_parser().parse_args(
-["--rootA", "", "--rootB", patched_source])
+args = CmpRuns.generate_option_parser().parse_args(
+["--root-old", "", "--root-new", patched_source, "", ""])
 # Scan the results, delete empty plist files.
 num_diffs, reports_in_ref, reports_in_new = \
-CmpRuns.dumpScanBuildResultsDiff(ref_dir, new_dir, opts,
- deleteEmpty=False,
- Stdout=LOCAL.stdout)
+CmpRuns.dump_scan_build_results_diff(ref_dir, new_dir, args,
+ delete_empty=False,
+ out=LOCAL.stdout)
 
 if num_diffs > 0:
 stdout(f"Warning: {num_diffs} differences in diagnostics.\n")
@@ -834,7 +834,8 @@
 plist = os.path.join(output_dir, plist)
 
 try:
-data = plistlib.readPlist(plist)
+with open(plist, "rb") as plist_file:
+data = plistlib.load(plist_file)
 # Delete empty reports.
 if not data['files']:
 os.remove(plist)
Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -17,26 +17,35 @@
 # Load the results of both runs, to obtain lists of the corresponding
 # AnalysisDiagnostic objects.
 #
-resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
-resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
+resultsA = load_results_from_single_run(singleRunInfoA, delete_empty)
+resultsB = load_results_from_single_run(singleRunInfoB, delete_empty)
 
 # Generate a relation from diagnostics in run A to diagnostics in run B
 # to obtain a list of triples (a, b, confidence).
-diff = compareResults(resultsA, resultsB)
+diff = compare_results(resultsA, resultsB)
 
 """
-from __future__ import division, print_function
-
-from collections import defaultdict
-
-from math import log
-from optparse import OptionParser
+import argparse
 import json
 import os
 import plistlib
 import re
 import sys
 
+from math import log
+from collections import defaultdict
+from copy import copy
+from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
+Tuple, Union)
+
+
+Number = Union[int, float]
+Stats = Dict[str, Dict[str, Number]]
+Plist = Dict[str, Any]
+JSON = Dict[str, Any]
+# Type for generics
+T = TypeVar('T')
+
 STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL)
 
 
@@ -56,118 +65,127 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path, root="", verboseLog=None):
+def __init__(self, path: str, root: str = "", verbose_log=None):
 self.path = path
 self.root = root.rstrip("/\\")
-self.verboseLog = verboseLog
+self.verbose_log = verbose_log
 
 
 class AnalysisDiagnostic:
-def __init__(self, data, report, htmlReport):
+def __init__(self, data: Plist, report: "AnalysisReport",
+ html_report: Optional[str]):
 self._data = data
 self._loc = self._data['location']
 self._report = report
-self._htmlReport = htmlReport
-self._reportSize = len(self._data['path'])
+self._html_report = html_report
+self._report_size = len(self._data['path'])
 
-def getFileName(self):
+def get_file_name(self) -> str:
 root = self._report.run.root
-fileName = self._report.files[self._loc['file']]
-if fileName.startswith(root) and len(root) > 0:
-return fileName[len(root) + 1:]
-return fileName
+file_name = self._report.files[self._loc['file']]
+
+if file_name.startswith(root) and len(root) > 0:
+return file_name[len(root) + 1:]
 
-def getRootFileName(self):
+return file_name
+
+def get_root_file_name(self) -> str:
 path = self._data['path']
+
 if not path:
-return self.getFileName()
+return self.get_file_name

[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266551.
vsavchenko added a comment.

Move 0 % x case to SValBuilder


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  clang/test/Analysis/PR35418.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/uninit-bug-first-iteration-init.c

Index: clang/test/Analysis/uninit-bug-first-iteration-init.c
===
--- /dev/null
+++ clang/test/Analysis/uninit-bug-first-iteration-init.c
@@ -0,0 +1,27 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/44978988
+// expected-no-diagnostics
+
+int foo();
+
+int gTotal;
+
+double bar(int start, int end) {
+  int i, cnt, processed, size;
+  double result, inc;
+
+  result = 0;
+  processed = start;
+  size = gTotal * 2;
+  cnt = (end - start + 1) * size;
+
+  for (i = 0; i < cnt; i += 2) {
+if ((i % size) == 0) {
+  inc = foo();
+  processed++;
+}
+result += inc * inc; // no-warning
+  }
+  return result;
+}
Index: clang/test/Analysis/constant-folding.c
===
--- clang/test/Analysis/constant-folding.c
+++ clang/test/Analysis/constant-folding.c
@@ -1,5 +1,9 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s
 
+#define UINT_MAX (~0U)
+#define INT_MAX (int)(UINT_MAX & (UINT_MAX >> 1))
+#define INT_MIN (int)(UINT_MAX & ~(UINT_MAX >> 1))
+
 void clang_analyzer_eval(int);
 
 // There should be no warnings unless otherwise indicated.
@@ -174,3 +178,69 @@
 clang_analyzer_eval((a & 1) <= 1); // expected-warning{{TRUE}}
   }
 }
+
+void testRemainderRules(unsigned int a, unsigned int b, int c, int d) {
+  // Check that we know that remainder of zero divided by any number is still 0.
+  clang_analyzer_eval((0 % c) == 0); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval((10 % a) <= 10); // expected-warning{{TRUE}}
+
+  if (a <= 30 && b <= 50) {
+clang_analyzer_eval((40 % a) < 30); // expected-warning{{TRUE}}
+clang_analyzer_eval((a % b) < 50);  // expected-warning{{TRUE}}
+clang_analyzer_eval((b % a) < 30);  // expected-warning{{TRUE}}
+
+if (a >= 10) {
+  // Even though it seems like a valid assumption, it is not.
+  // Check that we are not making this mistake.
+  clang_analyzer_eval((a % b) >= 10); // expected-warning{{UNKNOWN}}
+
+  // Check that we can we can infer when remainder is equal
+  // to the dividend.
+  clang_analyzer_eval((4 % a) == 4); // expected-warning{{TRUE}}
+  if (b < 7) {
+clang_analyzer_eval((b % a) < 7); // expected-warning{{TRUE}}
+  }
+}
+  }
+
+  if (c > -10) {
+clang_analyzer_eval((d % c) < INT_MAX); // expected-warning{{TRUE}}
+clang_analyzer_eval((d % c) > INT_MIN + 1); // expected-warning{{TRUE}}
+  }
+
+  // Check that we can reason about signed integers when they are
+  // known to be positive.
+  if (c >= 10 && c <= 30 && d >= 20 && d <= 50) {
+clang_analyzer_eval((5 % c) == 5);  // expected-warning{{TRUE}}
+clang_analyzer_eval((c % d) <= 30); // expected-warning{{TRUE}}
+clang_analyzer_eval((c % d) >= 0);  // expected-warning{{TRUE}}
+clang_analyzer_eval((d % c) < 30);  // expected-warning{{TRUE}}
+clang_analyzer_eval((d % c) >= 0);  // expected-warning{{TRUE}}
+  }
+
+  if (c >= -30 && c <= -10 && d >= -20 && d <= 50) {
+// Test positive LHS with negative RHS.
+clang_analyzer_eval((40 % c) < 30);  // expected-warning{{TRUE}}
+clang_analyzer_eval((40 % c) > -30); // expected-warning{{TRUE}}
+
+// Test negative LHS with possibly negative RHS.
+clang_analyzer_eval((-10 % d) < 50);  // expected-warning{{TRUE}}
+clang_analyzer_eval((-20 % d) > -50); // expected-warning{{TRUE}}
+
+// Check that we don't make wrong assumptions
+clang_analyzer_eval((-20 % d) > -20); // expected-warning{{UNKNOWN}}
+
+// Check that we can reason about negative ranges...
+clang_analyzer_eval((c % d) < 50); // expected-warning{{TRUE}}
+/// ...both ways
+clang_analyzer_eval((d % c) < 30); // expected-warning{{TRUE}}
+
+if (a <= 10) {
+  // Result is unsigned.  This means that 'c' is casted to unsigned.
+  // We don't want to reason about ranges changing boundaries with
+  // conversions.
+  clang_analyzer_eval((a % c) < 30); // expected-warning{{UNKNOWN}}
+}
+  }
+}
Index: clang/test/Analysis/PR35418.cpp
===
--- /dev/null
+++ clang/test/Analysis/PR35418.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// expected-no-diagnostics
+
+void halt() __attribute__((__noreturn__));
+void assert(int b) {
+  if (!b)

[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked 5 inline comments as done.
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:459-469
+if (Origin.From().isMinSignedValue()) {
+  // If mini is a minimal signed value, absolute value of it is greater
+  // than the maximal signed value.  In order to avoid these
+  // complications, we simply return the whole range.
+  return {ValueFactory.getMinValue(RangeType),
+  ValueFactory.getMaxValue(RangeType)};
+}

ASDenysPetrov wrote:
> I think you should swap `if` statements. I'll explain.
> Let's consider the input is an **uint8** range [42, 242] and you will return 
> [0, 242] in the second `if`.
> But if the input is an **uint8** range [128, 242] you will return [0, 255] in 
> the first `if`, because 128 is an equivalent of -128(INT8_MIN) in binary 
> representation so the condition in the first if would be true.
> What is the great difference between [42, 242] and [128, 242] to have 
> different results? Or you've just missed this case?
> 
> P.S. I think your function's name doesn't fit its body, since //absolute 
> value// is always positive (without sign) from its definition, but you output 
> range may have negative values. You'd better write an explanation above the 
> function and rename it.
It is a valid point, I will add this test and change this behaviour!

The name is confusing indeed, maybe you have any ideas what would be more 
appropriate?



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:481
+//   * Otherwise, From <= 0, To >= 0, and
+// AbsMax == max(abs(From), abs(To))
+llvm::APSInt AbsMax = std::max(-Origin.From(), Origin.To());

ASDenysPetrov wrote:
> As for me, the last  //reason// fully covers previous special cases, so you 
> can omit those ones, thus simplify the comment.
I really want to be clear about the first two cases to explain why this works 
for any sign of `From` and `To`



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:633
+
+  // Check if LHS is 0.  It's a special case when the result is guaranteed
+  // to be 0 no matter what RHS is (we put to the side the case when RHS is

xazax.hun wrote:
> I wonder if we actually need this? I vaguely recall that we are doing a lot 
> of simplifications during building symbolic expressions. I would be surprised 
> if this identity is not handled there. (And in that case, probable this 
> should be added there.) Or we might need a comment to explain why do we need 
> this simplification at both places. 
Yeah, we don't do it in `SValBuilder`, but it is definitely a better place for 
that particular case. I'll move it.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:659
+  //
+  // If we are dealing with unsigned case, we shouldn't move the lower bound.
+  if (Min.isSigned()) {

ASDenysPetrov wrote:
> Extend the comment, please, why we should move bounds to zero at all.
Good point!



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:684
+
+  return {RangeFactory, ValueFactory.getValue(Min), 
ValueFactory.getValue(Max)};
+}

ASDenysPetrov wrote:
> Is it OK to return this rangeset in case when one of operands(or both) is 
> negative, since this rangeset can vary from specific implementation?
Yes, it is a conservative range for any ranges because only the sign of the 
operation is specific to different implementations


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D79232: [analyzer] Refactor range inference for symbolic expressions

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266571.
vsavchenko added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D79232/new/

https://reviews.llvm.org/D79232

Files:
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/double-ranges-bug.c

Index: clang/test/Analysis/double-ranges-bug.c
===
--- /dev/null
+++ clang/test/Analysis/double-ranges-bug.c
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 -verify %s -analyzer-checker=core
+
+// expected-no-diagnostics
+
+typedef unsigned long int A;
+
+extern int fill(A **values, int *nvalues);
+
+void foo() {
+  A *values;
+  int nvalues;
+  fill(&values, &nvalues);
+
+  int i = 1;
+  double x, y;
+
+  y = values[i - 1];
+  x = values[i];
+
+  if (x <= y) {
+  }
+}
Index: clang/test/Analysis/constant-folding.c
===
--- clang/test/Analysis/constant-folding.c
+++ clang/test/Analysis/constant-folding.c
@@ -115,7 +115,22 @@
 #endif
 
   // Check that dynamically computed constants also work.
-  int constant = 1 << 3;
+  unsigned int constant = 1 << 3;
   unsigned int d = a | constant;
-  clang_analyzer_eval(constant > 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(d >= constant); // expected-warning{{TRUE}}
+
+  // Check that nested expressions also work.
+  clang_analyzer_eval(((a | 10) | 5) >= 10); // expected-warning{{TRUE}}
+
+  // TODO: We misuse intersection of ranges for bitwise AND and OR operators.
+  //   Resulting ranges for the following cases are infeasible.
+  //   This is what causes paradoxical results below.
+  if (a > 10) {
+clang_analyzer_eval((a & 1) <= 1); // expected-warning{{FALSE}}
+clang_analyzer_eval((a & 1) > 1);  // expected-warning{{FALSE}}
+  }
+  if (a < 10) {
+clang_analyzer_eval((a | 20) >= 20); // expected-warning{{FALSE}}
+clang_analyzer_eval((a | 20) < 20);  // expected-warning{{FALSE}}
+  }
 }
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -16,6 +16,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/ImmutableSet.h"
 #include "llvm/Support/raw_ostream.h"
@@ -23,10 +24,16 @@
 using namespace clang;
 using namespace ento;
 
+//===--===//
+//   RangeSet implementation
+//===--===//
+
 void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
-  const llvm::APSInt &Lower, const llvm::APSInt &Upper,
-  PrimRangeSet &newRanges, PrimRangeSet::iterator &i,
-  PrimRangeSet::iterator &e) const {
+const llvm::APSInt &Lower,
+const llvm::APSInt &Upper,
+PrimRangeSet &newRanges,
+PrimRangeSet::iterator &i,
+PrimRangeSet::iterator &e) const {
   // There are six cases for each range R in the set:
   //   1. R is entirely before the intersection range.
   //   2. R is entirely after the intersection range.
@@ -66,6 +73,11 @@
 }
 
 bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+  if (isEmpty()) {
+// This range is already infeasible.
+return false;
+  }
+
   // This function has nine cases, the cartesian product of range-testing
   // both the upper and lower bounds against the symbol's type.
   // Each case requires a different pinning operation.
@@ -283,6 +295,207 @@
 }
 
 namespace {
+
+/// A little component aggregating all of the reasoning we have about
+/// the ranges of symbolic expressions.
+///
+/// Even when we don't know the exact values of the operands, we still
+/// can get a pretty good estimate of the result's range.
+class SymbolicRangeInferrer
+: public SymExprVisitor {
+public:
+  static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolRef Sym) {
+SymbolicRangeInferrer Inferrer(BV, F, State);
+return Inferrer.infer(Sym);
+  }
+
+  RangeSet VisitSymExpr(SymbolRef Sym) {
+// If we got to this function, the actual type of the symbolic
+// expression is not supported for adv

[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266586.
vsavchenko added a comment.

Fix code review remarks


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  clang/test/Analysis/PR35418.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/uninit-bug-first-iteration-init.c

Index: clang/test/Analysis/uninit-bug-first-iteration-init.c
===
--- /dev/null
+++ clang/test/Analysis/uninit-bug-first-iteration-init.c
@@ -0,0 +1,27 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/44978988
+// expected-no-diagnostics
+
+int foo();
+
+int gTotal;
+
+double bar(int start, int end) {
+  int i, cnt, processed, size;
+  double result, inc;
+
+  result = 0;
+  processed = start;
+  size = gTotal * 2;
+  cnt = (end - start + 1) * size;
+
+  for (i = 0; i < cnt; i += 2) {
+if ((i % size) == 0) {
+  inc = foo();
+  processed++;
+}
+result += inc * inc; // no-warning
+  }
+  return result;
+}
Index: clang/test/Analysis/constant-folding.c
===
--- clang/test/Analysis/constant-folding.c
+++ clang/test/Analysis/constant-folding.c
@@ -1,5 +1,9 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s
 
+#define UINT_MAX (~0U)
+#define INT_MAX (int)(UINT_MAX & (UINT_MAX >> 1))
+#define INT_MIN (int)(UINT_MAX & ~(UINT_MAX >> 1))
+
 void clang_analyzer_eval(int);
 
 // There should be no warnings unless otherwise indicated.
@@ -174,3 +178,76 @@
 clang_analyzer_eval((a & 1) <= 1); // expected-warning{{TRUE}}
   }
 }
+
+void testRemainderRules(unsigned int a, unsigned int b, int c, int d) {
+  // Check that we know that remainder of zero divided by any number is still 0.
+  clang_analyzer_eval((0 % c) == 0); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval((10 % a) <= 10); // expected-warning{{TRUE}}
+
+  if (a <= 30 && b <= 50) {
+clang_analyzer_eval((40 % a) < 30); // expected-warning{{TRUE}}
+clang_analyzer_eval((a % b) < 50);  // expected-warning{{TRUE}}
+clang_analyzer_eval((b % a) < 30);  // expected-warning{{TRUE}}
+
+if (a >= 10) {
+  // Even though it seems like a valid assumption, it is not.
+  // Check that we are not making this mistake.
+  clang_analyzer_eval((a % b) >= 10); // expected-warning{{UNKNOWN}}
+
+  // Check that we can we can infer when remainder is equal
+  // to the dividend.
+  clang_analyzer_eval((4 % a) == 4); // expected-warning{{TRUE}}
+  if (b < 7) {
+clang_analyzer_eval((b % a) < 7); // expected-warning{{TRUE}}
+  }
+}
+  }
+
+  if (c > -10) {
+clang_analyzer_eval((d % c) < INT_MAX); // expected-warning{{TRUE}}
+clang_analyzer_eval((d % c) > INT_MIN + 1); // expected-warning{{TRUE}}
+  }
+
+  // Check that we can reason about signed integers when they are
+  // known to be positive.
+  if (c >= 10 && c <= 30 && d >= 20 && d <= 50) {
+clang_analyzer_eval((5 % c) == 5);  // expected-warning{{TRUE}}
+clang_analyzer_eval((c % d) <= 30); // expected-warning{{TRUE}}
+clang_analyzer_eval((c % d) >= 0);  // expected-warning{{TRUE}}
+clang_analyzer_eval((d % c) < 30);  // expected-warning{{TRUE}}
+clang_analyzer_eval((d % c) >= 0);  // expected-warning{{TRUE}}
+  }
+
+  if (c >= -30 && c <= -10 && d >= -20 && d <= 50) {
+// Test positive LHS with negative RHS.
+clang_analyzer_eval((40 % c) < 30);  // expected-warning{{TRUE}}
+clang_analyzer_eval((40 % c) > -30); // expected-warning{{TRUE}}
+
+// Test negative LHS with possibly negative RHS.
+clang_analyzer_eval((-10 % d) < 50);  // expected-warning{{TRUE}}
+clang_analyzer_eval((-20 % d) > -50); // expected-warning{{TRUE}}
+
+// Check that we don't make wrong assumptions
+clang_analyzer_eval((-20 % d) > -20); // expected-warning{{UNKNOWN}}
+
+// Check that we can reason about negative ranges...
+clang_analyzer_eval((c % d) < 50); // expected-warning{{TRUE}}
+/// ...both ways
+clang_analyzer_eval((d % c) < 30); // expected-warning{{TRUE}}
+
+if (a <= 10) {
+  // Result is unsigned.  This means that 'c' is casted to unsigned.
+  // We don't want to reason about ranges changing boundaries with
+  // conversions.
+  clang_analyzer_eval((a % c) < 30); // expected-warning{{UNKNOWN}}
+}
+  }
+
+  // Check that we work correctly when minimal unsigned value from a range is
+  // equal to the signed minimum for the same bit width.
+  unsigned int x = INT_MIN;
+  if (a >= x && a <= x + 10) {
+clang_analyzer_eval((b % a) < x + 10); // expected-warning{{TRUE}}
+  }
+}
Index: clang/test/Analysis/PR35418.cpp

[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-27 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked an inline comment as done.
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:459-469
+if (Origin.From().isMinSignedValue()) {
+  // If mini is a minimal signed value, absolute value of it is greater
+  // than the maximal signed value.  In order to avoid these
+  // complications, we simply return the whole range.
+  return {ValueFactory.getMinValue(RangeType),
+  ValueFactory.getMaxValue(RangeType)};
+}

vsavchenko wrote:
> ASDenysPetrov wrote:
> > I think you should swap `if` statements. I'll explain.
> > Let's consider the input is an **uint8** range [42, 242] and you will 
> > return [0, 242] in the second `if`.
> > But if the input is an **uint8** range [128, 242] you will return [0, 255] 
> > in the first `if`, because 128 is an equivalent of -128(INT8_MIN) in binary 
> > representation so the condition in the first if would be true.
> > What is the great difference between [42, 242] and [128, 242] to have 
> > different results? Or you've just missed this case?
> > 
> > P.S. I think your function's name doesn't fit its body, since //absolute 
> > value// is always positive (without sign) from its definition, but you 
> > output range may have negative values. You'd better write an explanation 
> > above the function and rename it.
> It is a valid point, I will add this test and change this behaviour!
> 
> The name is confusing indeed, maybe you have any ideas what would be more 
> appropriate?
@NoQ , @ASDenysPetrov what do you think about this name instead (i.e. 
`getSymmetricalRange`).


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Performance-wise, I've investigated huge slowdowns on `tmux` and `pytorch`.

- `pytorch` build produces a lot of warnings and simply trashed my terminal.  I 
guess one time it had more troubles with displaying all that than the other.  
Here is a table with new times:

|   | pytorch |
| - | --- |
| Time (before) | 2h21m33s|
| Time (after)  | 2h19m23s|
| Delta | {icon info-circle color=blue} -1.5% |
|

As you can see, these numbers are //way// smaller than the original ones.

- `tmux` is a much smaller project, so I decided to run it **20** times for 
each case.

F12018491: boxplot.png 
//After// consistently shows slower runtimes, but the overall difference (for 
median times) is only **+3%**.

I believe that as of now we can submit these modifications as is and explore 
performance optimizations later if needed.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D78990: [analyzer] Allow bindings of the CompoundLiteralRegion

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGbd06c417e6c7: [analyzer] Allow bindings of the 
CompoundLiteralRegion (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78990/new/

https://reviews.llvm.org/D78990

Files:
  clang/lib/StaticAnalyzer/Core/RegionStore.cpp
  clang/test/Analysis/compound-literals.c
  clang/test/Analysis/retain-release-compound-literal.m
  clang/unittests/StaticAnalyzer/StoreTest.cpp

Index: clang/unittests/StaticAnalyzer/StoreTest.cpp
===
--- clang/unittests/StaticAnalyzer/StoreTest.cpp
+++ clang/unittests/StaticAnalyzer/StoreTest.cpp
@@ -15,89 +15,139 @@
 namespace ento {
 namespace {
 
+class StoreTestConsumer : public ExprEngineConsumer {
+public:
+  StoreTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
+
+  bool HandleTopLevelDecl(DeclGroupRef DG) override {
+for (const auto *D : DG)
+  performTest(D);
+return true;
+  }
+
+private:
+  virtual void performTest(const Decl *D) = 0;
+};
+
+template  class TestAction : public ASTFrontendAction {
+public:
+  std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef File) override {
+return std::make_unique(Compiler);
+  }
+};
+
 // Test that we can put a value into an int-type variable and load it
 // back from that variable. Test what happens if default bindings are used.
-class VariableBindConsumer : public ExprEngineConsumer {
-  void performTest(const Decl *D) {
-StoreManager &StMgr = Eng.getStoreManager();
-SValBuilder &SVB = Eng.getSValBuilder();
-MemRegionManager &MRMgr = StMgr.getRegionManager();
-const ASTContext &ACtx = Eng.getContext();
+class VariableBindConsumer : public StoreTestConsumer {
+  void performTest(const Decl *D) override {
+StoreManager &SManager = Eng.getStoreManager();
+SValBuilder &Builder = Eng.getSValBuilder();
+MemRegionManager &MRManager = SManager.getRegionManager();
+const ASTContext &ASTCtxt = Eng.getContext();
 
 const auto *VDX0 = findDeclByName(D, "x0");
 const auto *VDY0 = findDeclByName(D, "y0");
 const auto *VDZ0 = findDeclByName(D, "z0");
 const auto *VDX1 = findDeclByName(D, "x1");
 const auto *VDY1 = findDeclByName(D, "y1");
-assert(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1);
+
+ASSERT_TRUE(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1);
 
 const StackFrameContext *SFC =
 Eng.getAnalysisDeclContextManager().getStackFrame(D);
 
-Loc LX0 = loc::MemRegionVal(MRMgr.getVarRegion(VDX0, SFC));
-Loc LY0 = loc::MemRegionVal(MRMgr.getVarRegion(VDY0, SFC));
-Loc LZ0 = loc::MemRegionVal(MRMgr.getVarRegion(VDZ0, SFC));
-Loc LX1 = loc::MemRegionVal(MRMgr.getVarRegion(VDX1, SFC));
-Loc LY1 = loc::MemRegionVal(MRMgr.getVarRegion(VDY1, SFC));
+Loc LX0 = loc::MemRegionVal(MRManager.getVarRegion(VDX0, SFC));
+Loc LY0 = loc::MemRegionVal(MRManager.getVarRegion(VDY0, SFC));
+Loc LZ0 = loc::MemRegionVal(MRManager.getVarRegion(VDZ0, SFC));
+Loc LX1 = loc::MemRegionVal(MRManager.getVarRegion(VDX1, SFC));
+Loc LY1 = loc::MemRegionVal(MRManager.getVarRegion(VDY1, SFC));
 
-Store StInit = StMgr.getInitialStore(SFC).getStore();
-SVal Zero = SVB.makeZeroVal(ACtx.IntTy);
-SVal One = SVB.makeIntVal(1, ACtx.IntTy);
-SVal NarrowZero = SVB.makeZeroVal(ACtx.CharTy);
+Store StInit = SManager.getInitialStore(SFC).getStore();
+SVal Zero = Builder.makeZeroVal(ASTCtxt.IntTy);
+SVal One = Builder.makeIntVal(1, ASTCtxt.IntTy);
+SVal NarrowZero = Builder.makeZeroVal(ASTCtxt.CharTy);
 
 // Bind(Zero)
-Store StX0 =
-StMgr.Bind(StInit, LX0, Zero).getStore();
-ASSERT_EQ(Zero, StMgr.getBinding(StX0, LX0, ACtx.IntTy));
+Store StX0 = SManager.Bind(StInit, LX0, Zero).getStore();
+EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy));
 
 // BindDefaultInitial(Zero)
 Store StY0 =
-StMgr.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
-ASSERT_EQ(Zero, StMgr.getBinding(StY0, LY0, ACtx.IntTy));
-ASSERT_EQ(Zero, *StMgr.getDefaultBinding(StY0, LY0.getAsRegion()));
+SManager.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
+EXPECT_EQ(Zero, SManager.getBinding(StY0, LY0, ASTCtxt.IntTy));
+EXPECT_EQ(Zero, *SManager.getDefaultBinding(StY0, LY0.getAsRegion()));
 
 // BindDefaultZero()
-Store StZ0 =
-StMgr.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
+Store StZ0 = SManager.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
 // BindDefaultZero wipes the region with '0 S8b', not with out Zero.
 // Direct load, however, does give us back the object of the type
 // that we specify for loading.
-ASSERT_EQ(Zero, StMgr.getBinding(StZ0, LZ0, ACtx.IntTy));
-ASSERT_EQ(NarrowZero, *StMgr.getDefaultBinding(St

[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked an inline comment as done.
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:506-507
+  ///
+  /// where abs(Origin) is the maximal absolute value of any possible values
+  ///   from Origin, and min(T) is a minimal value for the type T.
+  ///

NoQ wrote:
> I suggest not trying to express signed types and unsigned types in a single 
> formula, the reader will have to unwrap it back into the two cases anyway in 
> order to understand what's going on.
> 
> The following would imho be easier to read: "If T is signed, return the 
> smallest range `[-x..x]` that covers the original range, or `[-min(T), 
> max(T)]` if the aforementioned symmetric range doesn't exist due to original 
> range covering `min(T)`). If T is unsigned, return the smallest range 
> `[0..x]` that covers the original range".
That is a perfect explanation, thanks!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 266890.
vsavchenko added a comment.

Fix code review remarks


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  clang/test/Analysis/PR35418.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/hangs.c
  clang/test/Analysis/uninit-bug-first-iteration-init.c

Index: clang/test/Analysis/uninit-bug-first-iteration-init.c
===
--- /dev/null
+++ clang/test/Analysis/uninit-bug-first-iteration-init.c
@@ -0,0 +1,27 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/44978988
+// expected-no-diagnostics
+
+int foo();
+
+int gTotal;
+
+double bar(int start, int end) {
+  int i, cnt, processed, size;
+  double result, inc;
+
+  result = 0;
+  processed = start;
+  size = gTotal * 2;
+  cnt = (end - start + 1) * size;
+
+  for (i = 0; i < cnt; i += 2) {
+if ((i % size) == 0) {
+  inc = foo();
+  processed++;
+}
+result += inc * inc; // no-warning
+  }
+  return result;
+}
Index: clang/test/Analysis/hangs.c
===
--- clang/test/Analysis/hangs.c
+++ clang/test/Analysis/hangs.c
@@ -1,9 +1,16 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
-
-// expected-no-diagnostics
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker core,debug.ExprInspection
 
 // Stuff that used to hang.
 
+extern void __assert_fail(__const char *__assertion, __const char *__file,
+  unsigned int __line, __const char *__function)
+__attribute__((__noreturn__));
+#define assert(expr) \
+  ((expr) ? (void)(0) : __assert_fail(#expr, __FILE__, __LINE__, __func__))
+
+void clang_analyzer_eval(int);
+
 int g();
 
 int f(int y) {
@@ -28,3 +35,186 @@
   x += y; y += x + g();
   x += y; y += x + g();
 }
+
+void produce_an_exponentially_exploding_symbol_2(int x, int y) {
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  if (x > 1) {
+if (x > 2) {
+  if (x > 3) {
+if (x > 4) {
+  if (x > 5) {
+if (x > 6) {
+  if (x > 7) {
+if (x > 8) {
+  if (x > 9) {
+if (x > 10) {
+}
+  }
+}
+  }
+}
+  }
+}
+  }
+}
+  }
+}
+
+void produce_an_exponentially_exploding_symbol_3(int x, int y) {
+  assert(0 < x && x < 10);
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  clang_analyzer_eval(0 < x && x < 10); // expected-warning{{TRUE}}
+// expected-warning@-1{{FALSE}}
+}
Index: clang/test/Analysis/constant-folding.c
==

[PATCH] D79156: [analyzer] Merge implementations of SymInt, IntSym, and SymSym exprs

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGbb2ae74717a2: [analyzer] Merge implementations of SymInt, 
IntSym, and SymSym exprs (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D79156/new/

https://reviews.llvm.org/D79156

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
  clang/lib/StaticAnalyzer/Core/SymbolManager.cpp

Index: clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -34,45 +34,27 @@
 
 void SymExpr::anchor() {}
 
-LLVM_DUMP_METHOD void SymExpr::dump() const {
-  dumpToStream(llvm::errs());
-}
+LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }
 
-void SymIntExpr::dumpToStream(raw_ostream &os) const {
-  os << '(';
-  getLHS()->dumpToStream(os);
-  os << ") "
- << BinaryOperator::getOpcodeStr(getOpcode()) << ' ';
-  if (getRHS().isUnsigned())
-os << getRHS().getZExtValue();
-  else
-os << getRHS().getSExtValue();
-  if (getRHS().isUnsigned())
-os << 'U';
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
+  OS << '(';
+  Sym->dumpToStream(OS);
+  OS << ')';
 }
 
-void IntSymExpr::dumpToStream(raw_ostream &os) const {
-  if (getLHS().isUnsigned())
-os << getLHS().getZExtValue();
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
+ const llvm::APSInt &Value) {
+  if (Value.isUnsigned())
+OS << Value.getZExtValue();
   else
-os << getLHS().getSExtValue();
-  if (getLHS().isUnsigned())
-os << 'U';
-  os << ' '
- << BinaryOperator::getOpcodeStr(getOpcode())
- << " (";
-  getRHS()->dumpToStream(os);
-  os << ')';
+OS << Value.getSExtValue();
+  if (Value.isUnsigned())
+OS << 'U';
 }
 
-void SymSymExpr::dumpToStream(raw_ostream &os) const {
-  os << '(';
-  getLHS()->dumpToStream(os);
-  os << ") "
- << BinaryOperator::getOpcodeStr(getOpcode())
- << " (";
-  getRHS()->dumpToStream(os);
-  os << ')';
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
+ BinaryOperator::Opcode Op) {
+  OS << ' ' << BinaryOperator::getOpcodeStr(Op) << ' ';
 }
 
 void SymbolCast::dumpToStream(raw_ostream &os) const {
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
===
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
@@ -326,136 +326,83 @@
 Kind k = SE->getKind();
 return k >= BEGIN_BINARYSYMEXPRS && k <= END_BINARYSYMEXPRS;
   }
-};
-
-/// Represents a symbolic expression like 'x' + 3.
-class SymIntExpr : public BinarySymExpr {
-  const SymExpr *LHS;
-  const llvm::APSInt& RHS;
 
-public:
-  SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op,
- const llvm::APSInt &rhs, QualType t)
-  : BinarySymExpr(SymIntExprKind, op, t), LHS(lhs), RHS(rhs) {
-assert(lhs);
+protected:
+  static unsigned computeOperandComplexity(const SymExpr *Value) {
+return Value->computeComplexity();
   }
-
-  void dumpToStream(raw_ostream &os) const override;
-
-  const SymExpr *getLHS() const { return LHS; }
-  const llvm::APSInt &getRHS() const { return RHS; }
-
-  unsigned computeComplexity() const override {
-if (Complexity == 0)
-  Complexity = 1 + LHS->computeComplexity();
-return Complexity;
+  static unsigned computeOperandComplexity(const llvm::APSInt &Value) {
+return 1;
   }
 
-  static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs,
-  BinaryOperator::Opcode op, const llvm::APSInt& rhs,
-  QualType t) {
-ID.AddInteger((unsigned) SymIntExprKind);
-ID.AddPointer(lhs);
-ID.AddInteger(op);
-ID.AddPointer(&rhs);
-ID.Add(t);
+  static const llvm::APSInt *getPointer(const llvm::APSInt &Value) {
+return &Value;
   }
+  static const SymExpr *getPointer(const SymExpr *Value) { return Value; }
 
-  void Profile(llvm::FoldingSetNodeID& ID) override {
-Profile(ID, LHS, getOpcode(), RHS, getType());
-  }
-
-  // Implement isa support.
-  static bool classof(const SymExpr *SE) {
-return SE->getKind() == SymIntExprKind;
-  }
+  static void dumpToStreamImpl(raw_ostream &os, const SymExpr *Value);
+  static void dumpToStreamImpl(raw_ostream &os, const llvm::APSInt &Value);
+  static void dumpToStreamImpl(raw_ostream &os, BinaryOperator::Opcode op);
 };
 
-/// Represents a symbolic expression like 3 - 'x'.
-class IntSymExpr : public BinarySymExpr {
-  const llvm::APSInt& LHS;
-  const SymExpr *RHS;
+/// Template implementation for all binary symbolic expressions
+template 
+class BinarySymExprImpl : public BinarySymExpr {
+  LHSTYPE LHS

[PATCH] D79434: [analyzer] Generalize bitwise AND rules for ranges

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG2a09daff0f90: [analyzer] Generalize bitwise AND rules for 
ranges (authored by vsavchenko).

Changed prior to commit:
  https://reviews.llvm.org/D79434?vs=264606&id=266911#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D79434/new/

https://reviews.llvm.org/D79434

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/switch-case.c
  clang/test/Analysis/uninit-exhaustive-switch-bug.c

Index: clang/test/Analysis/uninit-exhaustive-switch-bug.c
===
--- /dev/null
+++ clang/test/Analysis/uninit-exhaustive-switch-bug.c
@@ -0,0 +1,20 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/54359410
+// expected-no-diagnostics
+
+int rand();
+
+void test() {
+  int offset = 0;
+  int value;
+  int test = rand();
+  switch (test & 0x1) {
+  case 0:
+  case 1:
+value = 0;
+break;
+  }
+
+  offset += value; // no-warning
+}
Index: clang/test/Analysis/switch-case.c
===
--- clang/test/Analysis/switch-case.c
+++ clang/test/Analysis/switch-case.c
@@ -218,3 +218,14 @@
 break;
   }
 }
+
+void testExhaustiveSwitch(unsigned int a) {
+  switch (a & 5) {
+  case 0 ... 5:
+clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+break;
+  default:
+clang_analyzer_warnIfReached(); // no-warning
+break;
+  }
+}
Index: clang/test/Analysis/constant-folding.c
===
--- clang/test/Analysis/constant-folding.c
+++ clang/test/Analysis/constant-folding.c
@@ -78,19 +78,20 @@
 }
 
 void testBitwiseRules(unsigned int a, int b, int c) {
-  clang_analyzer_eval((a | 1) >= 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval((a | 1) >= 1);   // expected-warning{{TRUE}}
   clang_analyzer_eval((a | -1) >= -1); // expected-warning{{TRUE}}
-  clang_analyzer_eval((a | 2) >= 2); // expected-warning{{TRUE}}
-  clang_analyzer_eval((a | 5) >= 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval((a | 2) >= 2);   // expected-warning{{TRUE}}
+  clang_analyzer_eval((a | 5) >= 5);   // expected-warning{{TRUE}}
   clang_analyzer_eval((a | 10) >= 10); // expected-warning{{TRUE}}
 
   // Argument order should not influence this
   clang_analyzer_eval((1 | a) >= 1); // expected-warning{{TRUE}}
 
-  clang_analyzer_eval((a & 1) <= 1); // expected-warning{{TRUE}}
-  clang_analyzer_eval((a & 2) <= 2); // expected-warning{{TRUE}}
-  clang_analyzer_eval((a & 5) <= 5); // expected-warning{{TRUE}}
-  clang_analyzer_eval((a & 10) <= 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval((a & 1) <= 1);// expected-warning{{TRUE}}
+  clang_analyzer_eval((a & 1) >= 0);// expected-warning{{TRUE}}
+  clang_analyzer_eval((a & 2) <= 2);// expected-warning{{TRUE}}
+  clang_analyzer_eval((a & 5) <= 5);// expected-warning{{TRUE}}
+  clang_analyzer_eval((a & 10) <= 10);  // expected-warning{{TRUE}}
   clang_analyzer_eval((a & -10) <= 10); // expected-warning{{UNKNOWN}}
 
   // Again, check for different argument order.
@@ -104,22 +105,37 @@
   clang_analyzer_eval((b | 1) > 0); // expected-warning{{UNKNOWN}}
 
   // Even for signed values, bitwise OR with a non-zero is always non-zero.
-  clang_analyzer_eval((b | 1) == 0); // expected-warning{{FALSE}}
+  clang_analyzer_eval((b | 1) == 0);  // expected-warning{{FALSE}}
   clang_analyzer_eval((b | -2) == 0); // expected-warning{{FALSE}}
   clang_analyzer_eval((b | 10) == 0); // expected-warning{{FALSE}}
-  clang_analyzer_eval((b | 0) == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval((b | 0) == 0);  // expected-warning{{UNKNOWN}}
   clang_analyzer_eval((b | -2) >= 0); // expected-warning{{FALSE}}
 
   // Check that we can operate with negative ranges
   if (b < 0) {
 clang_analyzer_eval((b | -1) == -1);   // expected-warning{{TRUE}}
 clang_analyzer_eval((b | -10) >= -10); // expected-warning{{TRUE}}
+clang_analyzer_eval((b & 0) == 0); // expected-warning{{TRUE}}
+clang_analyzer_eval((b & -10) <= -10); // expected-warning{{TRUE}}
+clang_analyzer_eval((b & 5) >= 0); // expected-warning{{TRUE}}
 
 int e = (b | -5);
 clang_analyzer_eval(e >= -5 && e <= -1); // expected-warning{{TRUE}}
 
 if (b < -20) {
-  clang_analyzer_eval((b | e) >= -5); // expected-warning{{TRUE}}
+  clang_analyzer_eval((b | e) >= -5);// expected-warning{{TRUE}}
+  clang_analyzer_eval((b & -10) < -20);  // expected-warning{{TRUE}}
+  clang_analyzer_eval((b & e) < -20);// expected-warning{{TRUE}}
+  clang_analyzer_eval((b & -30) <= -30); // expected-warning{{TRUE}}
+
+  if (c >= -30 && c <= -10) {
+clang_analyzer_eval((b & c) <= -20); // expected-warning{{TRUE}}
+  }
+}
+
+if (a <= 40)

[PATCH] D79336: [analyzer] Generalize bitwise OR rules for ranges

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG47c4b8bd6869: [analyzer] Generalize bitwise OR rules for 
ranges (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D79336/new/

https://reviews.llvm.org/D79336

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/constant-folding.c

Index: clang/test/Analysis/constant-folding.c
===
--- clang/test/Analysis/constant-folding.c
+++ clang/test/Analysis/constant-folding.c
@@ -77,7 +77,7 @@
   clang_analyzer_eval(a != b); // expected-warning{{TRUE}}
 }
 
-void testBitwiseRules(unsigned int a, int b) {
+void testBitwiseRules(unsigned int a, int b, int c) {
   clang_analyzer_eval((a | 1) >= 1); // expected-warning{{TRUE}}
   clang_analyzer_eval((a | -1) >= -1); // expected-warning{{TRUE}}
   clang_analyzer_eval((a | 2) >= 2); // expected-warning{{TRUE}}
@@ -96,9 +96,9 @@
   // Again, check for different argument order.
   clang_analyzer_eval((1 & a) <= 1); // expected-warning{{TRUE}}
 
-  unsigned int c = a;
-  c |= 1;
-  clang_analyzer_eval((c | 0) == 0); // expected-warning{{FALSE}}
+  unsigned int d = a;
+  d |= 1;
+  clang_analyzer_eval((d | 0) == 0); // expected-warning{{FALSE}}
 
   // Rules don't apply to signed typed, as the values might be negative.
   clang_analyzer_eval((b | 1) > 0); // expected-warning{{UNKNOWN}}
@@ -108,20 +108,47 @@
   clang_analyzer_eval((b | -2) == 0); // expected-warning{{FALSE}}
   clang_analyzer_eval((b | 10) == 0); // expected-warning{{FALSE}}
   clang_analyzer_eval((b | 0) == 0); // expected-warning{{UNKNOWN}}
-#ifdef ANALYZER_CM_Z3
   clang_analyzer_eval((b | -2) >= 0); // expected-warning{{FALSE}}
-#else
-  clang_analyzer_eval((b | -2) >= 0); // expected-warning{{UNKNOWN}}
-#endif
+
+  // Check that we can operate with negative ranges
+  if (b < 0) {
+clang_analyzer_eval((b | -1) == -1);   // expected-warning{{TRUE}}
+clang_analyzer_eval((b | -10) >= -10); // expected-warning{{TRUE}}
+
+int e = (b | -5);
+clang_analyzer_eval(e >= -5 && e <= -1); // expected-warning{{TRUE}}
+
+if (b < -20) {
+  clang_analyzer_eval((b | e) >= -5); // expected-warning{{TRUE}}
+}
+
+// Check that we can reason about the result even if know nothing
+// about one of the operands.
+clang_analyzer_eval((b | c) != 0); // expected-warning{{TRUE}}
+  }
+
+  if (a <= 30 && b >= 10 && c >= 20) {
+// Check that we can reason about non-constant operands.
+clang_analyzer_eval((b | c) >= 20); // expected-warning{{TRUE}}
+
+// Check that we can reason about the resulting range even if
+// the types are not the same, but we still can convert operand
+// ranges.
+clang_analyzer_eval((a | b) >= 10); // expected-warning{{TRUE}}
+  }
 
   // Check that dynamically computed constants also work.
   unsigned int constant = 1 << 3;
-  unsigned int d = a | constant;
-  clang_analyzer_eval(d >= constant); // expected-warning{{TRUE}}
+  unsigned int f = a | constant;
+  clang_analyzer_eval(f >= constant); // expected-warning{{TRUE}}
 
   // Check that nested expressions also work.
   clang_analyzer_eval(((a | 10) | 5) >= 10); // expected-warning{{TRUE}}
 
+  if (a < 10) {
+clang_analyzer_eval((a | 20) >= 20); // expected-warning{{TRUE}}
+  }
+
   // TODO: We misuse intersection of ranges for bitwise AND and OR operators.
   //   Resulting ranges for the following cases are infeasible.
   //   This is what causes paradoxical results below.
@@ -129,8 +156,4 @@
 clang_analyzer_eval((a & 1) <= 1); // expected-warning{{FALSE}}
 clang_analyzer_eval((a & 1) > 1);  // expected-warning{{FALSE}}
   }
-  if (a < 10) {
-clang_analyzer_eval((a | 20) >= 20); // expected-warning{{FALSE}}
-clang_analyzer_eval((a | 20) < 20);  // expected-warning{{FALSE}}
-  }
 }
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -69,7 +69,19 @@
 
 const llvm::APSInt &RangeSet::getMinValue() const {
   assert(!isEmpty());
-  return ranges.begin()->From();
+  return begin()->From();
+}
+
+const llvm::APSInt &RangeSet::getMaxValue() const {
+  assert(!isEmpty());
+  // NOTE: It's a shame that we can't implement 'getMaxValue' without scanning
+  //   the whole tree to get to the last element.
+  //   llvm::ImmutableSet should support decrement for 'end' iterators
+  //   or reverse order iteration.
+  auto It = begin();
+  for (auto End = end(); std::next(It) != End; ++It) {
+  }
+  return It->To();
 }
 
 bool RangeSet::pin(llvm::APSInt &Low

[PATCH] D79232: [analyzer] Refactor range inference for symbolic expressions

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG1f57d76a8dd0: [analyzer] Refactor range inference for 
symbolic expressions (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D79232/new/

https://reviews.llvm.org/D79232

Files:
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/double-ranges-bug.c

Index: clang/test/Analysis/double-ranges-bug.c
===
--- /dev/null
+++ clang/test/Analysis/double-ranges-bug.c
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 -verify %s -analyzer-checker=core
+
+// expected-no-diagnostics
+
+typedef unsigned long int A;
+
+extern int fill(A **values, int *nvalues);
+
+void foo() {
+  A *values;
+  int nvalues;
+  fill(&values, &nvalues);
+
+  int i = 1;
+  double x, y;
+
+  y = values[i - 1];
+  x = values[i];
+
+  if (x <= y) {
+  }
+}
Index: clang/test/Analysis/constant-folding.c
===
--- clang/test/Analysis/constant-folding.c
+++ clang/test/Analysis/constant-folding.c
@@ -115,7 +115,22 @@
 #endif
 
   // Check that dynamically computed constants also work.
-  int constant = 1 << 3;
+  unsigned int constant = 1 << 3;
   unsigned int d = a | constant;
-  clang_analyzer_eval(constant > 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(d >= constant); // expected-warning{{TRUE}}
+
+  // Check that nested expressions also work.
+  clang_analyzer_eval(((a | 10) | 5) >= 10); // expected-warning{{TRUE}}
+
+  // TODO: We misuse intersection of ranges for bitwise AND and OR operators.
+  //   Resulting ranges for the following cases are infeasible.
+  //   This is what causes paradoxical results below.
+  if (a > 10) {
+clang_analyzer_eval((a & 1) <= 1); // expected-warning{{FALSE}}
+clang_analyzer_eval((a & 1) > 1);  // expected-warning{{FALSE}}
+  }
+  if (a < 10) {
+clang_analyzer_eval((a | 20) >= 20); // expected-warning{{FALSE}}
+clang_analyzer_eval((a | 20) < 20);  // expected-warning{{FALSE}}
+  }
 }
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -16,6 +16,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/ImmutableSet.h"
 #include "llvm/Support/raw_ostream.h"
@@ -23,10 +24,16 @@
 using namespace clang;
 using namespace ento;
 
+//===--===//
+//   RangeSet implementation
+//===--===//
+
 void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
-  const llvm::APSInt &Lower, const llvm::APSInt &Upper,
-  PrimRangeSet &newRanges, PrimRangeSet::iterator &i,
-  PrimRangeSet::iterator &e) const {
+const llvm::APSInt &Lower,
+const llvm::APSInt &Upper,
+PrimRangeSet &newRanges,
+PrimRangeSet::iterator &i,
+PrimRangeSet::iterator &e) const {
   // There are six cases for each range R in the set:
   //   1. R is entirely before the intersection range.
   //   2. R is entirely after the intersection range.
@@ -66,6 +73,11 @@
 }
 
 bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+  if (isEmpty()) {
+// This range is already infeasible.
+return false;
+  }
+
   // This function has nine cases, the cartesian product of range-testing
   // both the upper and lower bounds against the symbol's type.
   // Each case requires a different pinning operation.
@@ -283,6 +295,207 @@
 }
 
 namespace {
+
+/// A little component aggregating all of the reasoning we have about
+/// the ranges of symbolic expressions.
+///
+/// Even when we don't know the exact values of the operands, we still
+/// can get a pretty good estimate of the result's range.
+class SymbolicRangeInferrer
+: public SymExprVisitor {
+public:
+  static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolRef Sym) {
+SymbolicRangeInferrer Inferrer(BV, F, State);
+return Inferrer.infer(Sym);
+  }
+
+  RangeSet VisitSymExpr(SymbolRef Sym) {
+ 

[PATCH] D80117: [analyzer] Introduce reasoning about symbolic remainder operator

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG73c120a9895a: [analyzer] Introduce reasoning about symbolic 
remainder operator (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80117/new/

https://reviews.llvm.org/D80117

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  clang/test/Analysis/PR35418.cpp
  clang/test/Analysis/constant-folding.c
  clang/test/Analysis/hangs.c
  clang/test/Analysis/uninit-bug-first-iteration-init.c

Index: clang/test/Analysis/uninit-bug-first-iteration-init.c
===
--- /dev/null
+++ clang/test/Analysis/uninit-bug-first-iteration-init.c
@@ -0,0 +1,27 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/44978988
+// expected-no-diagnostics
+
+int foo();
+
+int gTotal;
+
+double bar(int start, int end) {
+  int i, cnt, processed, size;
+  double result, inc;
+
+  result = 0;
+  processed = start;
+  size = gTotal * 2;
+  cnt = (end - start + 1) * size;
+
+  for (i = 0; i < cnt; i += 2) {
+if ((i % size) == 0) {
+  inc = foo();
+  processed++;
+}
+result += inc * inc; // no-warning
+  }
+  return result;
+}
Index: clang/test/Analysis/hangs.c
===
--- clang/test/Analysis/hangs.c
+++ clang/test/Analysis/hangs.c
@@ -1,9 +1,16 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
-
-// expected-no-diagnostics
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker core,debug.ExprInspection
 
 // Stuff that used to hang.
 
+extern void __assert_fail(__const char *__assertion, __const char *__file,
+  unsigned int __line, __const char *__function)
+__attribute__((__noreturn__));
+#define assert(expr) \
+  ((expr) ? (void)(0) : __assert_fail(#expr, __FILE__, __LINE__, __func__))
+
+void clang_analyzer_eval(int);
+
 int g();
 
 int f(int y) {
@@ -28,3 +35,186 @@
   x += y; y += x + g();
   x += y; y += x + g();
 }
+
+void produce_an_exponentially_exploding_symbol_2(int x, int y) {
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  if (x > 1) {
+if (x > 2) {
+  if (x > 3) {
+if (x > 4) {
+  if (x > 5) {
+if (x > 6) {
+  if (x > 7) {
+if (x > 8) {
+  if (x > 9) {
+if (x > 10) {
+}
+  }
+}
+  }
+}
+  }
+}
+  }
+}
+  }
+}
+
+void produce_an_exponentially_exploding_symbol_3(int x, int y) {
+  assert(0 < x && x < 10);
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  x &= y;
+  y &= x & g();
+  clang_analyzer_eval(0 < x && x < 10); // expected-warning{{TRUE}}
+   

[PATCH] D80626: [analyzer] SATestBuild.py: Make verbosity level a cmd option

2020-05-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5395389475bc: [analyzer] SATestBuild.py: Make verbosity 
level a cmd option (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80626/new/

https://reviews.llvm.org/D80626

Files:
  clang/utils/analyzer/SATestBuild.py


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -389,7 +389,7 @@
 start_time = time.time()
 
 project_dir = self.get_project_dir()
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Build directory: {project_dir}.\n")
 
 # Set the build results directory.
@@ -431,7 +431,7 @@
 
 # Clean up scan build results.
 if os.path.exists(output_dir):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing old results: {output_dir}\n")
 
 shutil.rmtree(output_dir)
@@ -517,7 +517,7 @@
 
 command_to_run = command_prefix + command
 
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Executing: {command_to_run}\n")
 
 check_call(command_to_run, cwd=cwd,
@@ -575,7 +575,7 @@
 log_path = os.path.join(fail_path, file_name + ".stderr.txt")
 with open(log_path, "w+") as log_file:
 try:
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Executing: {command}\n")
 
 check_call(command, cwd=directory, stderr=log_file,
@@ -744,7 +744,7 @@
 for ref_dir, new_dir in zip(ref_list, new_list):
 assert(ref_dir != new_dir)
 
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Comparing Results: {ref_dir} {new_dir}\n")
 
 patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
@@ -818,7 +818,7 @@
 
 # Clean up the log file.
 if os.path.exists(build_log_path):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing log file: {build_log_path}\n")
 
 os.remove(build_log_path)
@@ -887,29 +887,31 @@
 
 if __name__ == "__main__":
 # Parse command line arguments.
-Parser = argparse.ArgumentParser(
+parser = argparse.ArgumentParser(
 description="Test the Clang Static Analyzer.")
 
-Parser.add_argument("--strictness", dest="strictness", type=int, default=0,
+parser.add_argument("--strictness", dest="strictness", type=int, default=0,
 help="0 to fail on runtime errors, 1 to fail when the "
 "number of found bugs are different from the "
 "reference, 2 to fail on any difference from the "
 "reference. Default is 0.")
-Parser.add_argument("-r", dest="regenerate", action="store_true",
+parser.add_argument("-r", dest="regenerate", action="store_true",
 default=False, help="Regenerate reference output.")
-Parser.add_argument("--override-compiler", action="store_true",
+parser.add_argument("--override-compiler", action="store_true",
 default=False, help="Call scan-build with "
 "--override-compiler option.")
-Parser.add_argument("-j", "--jobs", dest="jobs", type=int,
+parser.add_argument("-j", "--jobs", dest="jobs", type=int,
 default=0,
 help="Number of projects to test concurrently")
-Parser.add_argument("--extra-analyzer-config",
+parser.add_argument("--extra-analyzer-config",
 dest="extra_analyzer_config", type=str,
 default="",
 help="Arguments passed to to -analyzer-config")
+parser.add_argument("-v", "--verbose", action="count", default=0)
 
-args = Parser.parse_args()
+args = parser.parse_args()
 
+VERBOSE = args.verbose
 tester = RegressionTester(args.jobs, args.override_compiler,
   args.extra_analyzer_config, args.regenerate,
   args.strictness)


Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -389,7 +389,7 @@
 start_time = time.time()
 
 project_dir = self.get_project_dir()
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Build directory: {project_dir}.\n")
 
 # Set the build results directory.
@@ -431,7 +431,7 @@
 
 # Clean up scan build results.
 if os.path.exists(output_dir):
-if VERBOSE == 1:
+if VERBOSE >= 1:
 stdout(f"  Removing old results: {out

[PATCH] D78933: [analyzer] RangeConstraintManager optimizations in comparison expressions

2020-05-29 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Sorry for postponing this review.
I'll get to it right away!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78933/new/

https://reviews.llvm.org/D78933



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D78933: [analyzer] RangeConstraintManager optimizations in comparison expressions

2020-05-29 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:32
+BO_GE < BO_EQ && BO_EQ < BO_NE,
+"This class relies on operators order. Rework it otherwise.");
+

+1 for this static assert!
It's good to ensure such assumptions, even if those are very unlikely to change



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:80
+
+  static size_t IndexFromOp(BinaryOperatorKind OP) {
+return static_cast(OP - BO_LT);

I would prefer function names to comply with LLVM coding standards (start with 
a verb and a lowercase letter
https://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:665
+
+auto SSE = dyn_cast(Sym);
+if (!SSE)

I believe that when we use auto we still try to be more verbose with it, so in 
this case it should be smth like `const auto *SSE`



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:689
+
+// Loop goes through the all columns except the last `UnknownX2`
+// We treat `UnknownX2` column separately at the end of the loop body.

Sorry for nitpicking, but it seems like the grammar is a bit off in the **the 
all columns except the last** part



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:690
+// Loop goes through the all columns except the last `UnknownX2`
+// We treat `UnknownX2` column separately at the end of the loop body.
+for (size_t i = 0; i < CmpOpTable.GetCmpOpCount(); ++i) {

I think that **treat** is not the best option here. Maybe smith like 
**process** will do?



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:696
+  const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
+  const RangeSet *RangeSet = State->get(SymSym);
+

Maybe we can name this constant somehow differently to be more verbose? 



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:700-705
+  if (!RangeSet) {
+const BinaryOperatorKind ROP =
+BinaryOperator::reverseComparisonOp(QueriedOP);
+SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
+RangeSet = State->get(SymSym);
+  }

Please, correct me if I'm wrong, but I thought that we are iterating over 
//all// comparison operators (excluding `<=>`), which means that if we don't 
find it on this iteration for let's say `x < y` then we'll find it (or already 
did) for `x > y`.  So, my question is - why do we have this clause then?

And it confuses me that `RangeSet` now corresponds to a //reversed// operator, 
while `QueriedOP` remains the same.

Maybe a good commentary explaining why we need it could help!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78933/new/

https://reviews.llvm.org/D78933



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D78933: [analyzer] RangeConstraintManager optimizations in comparison expressions

2020-05-29 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Great job!  Thank you for your work!

I would say, fixing a couple of minor style issues and testing it on a few more 
projects, and it is ready to land!

> Another point is that I don't know how to get printed stats from the 
> scan-build.

That option in `scan-build` is called `-internal-stats`.

> I completely agree with you. But, unfortunately, vim-proj is the only I could 
> squeeze from that bunch.

My opinion here is that any projects (with a good amount of users or stars on 
GitHub ^-^ ) will do.  If it is hard to build them on Windows, move to the next 
project.  I guess projects with a minimal number of dependencies should be 
fairly easy on any platform.




Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:80
+
+  static size_t IndexFromOp(BinaryOperatorKind OP) {
+return static_cast(OP - BO_LT);

ASDenysPetrov wrote:
> vsavchenko wrote:
> > I would prefer function names to comply with LLVM coding standards (start 
> > with a verb and a lowercase letter
> > https://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly
> I'll add a **get **prefix. Saying about lower/uppercases, I'd say this is a 
> mess inside this file. I've just decided to use an upper one.
I agree that it is a mess, but if we want to reduce it with time, I'd say we 
should stick to the official style guide.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:689
+
+// Loop goes through the all columns except the last `UnknownX2`
+// We treat `UnknownX2` column separately at the end of the loop body.

ASDenysPetrov wrote:
> vsavchenko wrote:
> > Sorry for nitpicking, but it seems like the grammar is a bit off in the 
> > **the all columns except the last** part
> Do you mean to change to **all of the columns exept the last one 
> ('UnknownX2')**?
Yep, sounds good!



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:696
+  const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
+  const RangeSet *RangeSet = State->get(SymSym);
+

ASDenysPetrov wrote:
> vsavchenko wrote:
> > Maybe we can name this constant somehow differently to be more verbose? 
> What do you think about **FoundRangeSet**?
I was thinking about adding more context to the name, something like 
`RelatedRangeSet` or similar.  To show not only what it is (`RangeSet`) and how 
we got it (`found`), but also its purpose or meaning.



Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:700-705
+  if (!RangeSet) {
+const BinaryOperatorKind ROP =
+BinaryOperator::reverseComparisonOp(QueriedOP);
+SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
+RangeSet = State->get(SymSym);
+  }

ASDenysPetrov wrote:
> vsavchenko wrote:
> > Please, correct me if I'm wrong, but I thought that we are iterating over 
> > //all// comparison operators (excluding `<=>`), which means that if we 
> > don't find it on this iteration for let's say `x < y` then we'll find it 
> > (or already did) for `x > y`.  So, my question is - why do we have this 
> > clause then?
> > 
> > And it confuses me that `RangeSet` now corresponds to a //reversed// 
> > operator, while `QueriedOP` remains the same.
> > 
> > Maybe a good commentary explaining why we need it could help!
> No-no. Please, look carefully.
> At first we try to find `X op Y`, when **op** is a comparison operator. If we 
> failed then try to find the reversed (flipped) version of the expression `Y 
> reversed(op) X`.
> Examples: `x > y and y < x`, `x != y and y != x`, `x <= y and y >= x`
> 
> We don't care about operand positions, we just want to find any of its 
> previous relation, and which branch of it we are now in.
Oh, I see now. I missed the part where `RHS` is on the left now :-) 


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78933/new/

https://reviews.llvm.org/D78933



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80366: [Analyzer] Add `getReturnValueUnderConstruction()` to `CallEvent`

2020-06-02 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Core/CallEvent.cpp:552
+
+  Index = StackFrame->getIndex();
+

Szelethus wrote:
> baloghadamsoftware wrote:
> > Szelethus wrote:
> > > This mustn't be serious. `StackFrameContext::getIndex()` has **no 
> > > comments** at all! So it is an index to the element within a `CFGBlock` 
> > > to which it also stores a field for??? Ugh. Shouldn't we just have a 
> > > `getCallSiteCFGElement` or something? Especially since we have 
> > > `CFGElementRef`s now.
> > > 
> > > Would you be so nice to hunt this down please? :)
> > Do you mean a new `StackFrameContext::getCFGElement()` member function? I 
> > agree. I can do it, however this technology where we get the block and the 
> > index separately is used at many places in the code, then it would be 
> > appropriate to refactor all these places. Even wrose is the backward 
> > direction, where we look for the CFG element of a statement: we find the 
> > block from `CFGStmtMap` and then we search for the index **liearly**, 
> > instrad of storing it in the map as well!!!
> Nasty. Yeah, I mean not to burden you work refactoring any more then you feel 
> like doing it, but simply adding a `StackFrameContext::getCFGElement()` 
> method and using it in this patch would be nice enough, and some comments to 
> `getIndex()`. But this obviously isn't a high prio issue.
+1
I do believe that clearer interface functions and better segregation of 
different functionalities will make it much easier for us in the future



Comment at: clang/lib/StaticAnalyzer/Core/CallEvent.cpp:553
+
+  if(const auto Ctor = (*Block)[Index].getAs()) {
+return Ctor->getConstructionContext();

nit: space after `if`

And I would also prefer putting `const auto *Ctor`



Comment at: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:247-249
+  Optional ExistingVal = getObjectUnderConstruction(State, CE, LCtx);
+  if (ExistingVal.hasValue())
+return std::make_pair(State, *ExistingVal);

Maybe here (and further) we can use a widespread pattern of `bool`-castable 
things declared in the `if` condition (like you do with `dyn_casts`):

```
if (Optional ExistingVal = getObjectUnderConstruction(State, CE, LCtx))
  return std::make_pair(State, *ExistingVal);
```



Comment at: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:345-360
+  if (const auto *CE = dyn_cast(E)) {
+Optional ExistingVal =
+  getObjectUnderConstruction(State, {CE, Idx}, LCtx);
+if (ExistingVal.hasValue())
+  return std::make_pair(State, *ExistingVal);
+  } else if (const auto *CCE = dyn_cast(E)) {
+Optional ExistingVal =

It seems like a code duplication to me.

First of all we can first compose a `ConstructionContextItem` from either `CE`, 
`CCE`, or `ME` and then call for `getObjectUnderConstruction`.
Additionally, I think we can hide even that by introducing an overload for 
`getObjectUnderConstruction` that takes an expression and an index. 



Comment at: 
clang/unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp:24
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+if (!Call.getOriginExpr())

martong wrote:
> I assume this tests this call expression: `returnC(1)` . But this is 
> absolutely not trivial from the test code, could you please make this cleaner?
I think that this function, the meat and bones of the test, should be properly 
commented and state explicitly what are the assumption and what is the expected 
outcome.



Comment at: 
clang/unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp:29
+Optional RetVal = Call.getReturnValueUnderConstruction(0);
+assert(RetVal);
+assert(RetVal->getAsRegion());

martong wrote:
> I think it would make the tests cleaner if we had some member variables set 
> here and then in the test function 
> (`addTestReturnValueUnderConstructionChecker`) we had the expectations on 
> those variables.
> Right now, if we face an assertion it kills the whole unit test suite and it 
> is not obvious why that happened.
Why not `gtest`s assertions? Those will also interrupt the execution, but will 
be much clearer in the output.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80366/new/

https://reviews.llvm.org/D80366



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D83836: [Analyzer] Implementing checkRegionChanges for SmartPtrModeling

2020-07-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Additionally, I would prefer commit message to be imperative.  It is sorta like 
a rule of a good commit message.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83836/new/

https://reviews.llvm.org/D83836



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77062: [analyzer] Improved zero assumption in CStringChecke::assumeZero

2020-07-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

It is a good practice in many projects to make commit messages into imperative 
(i.e. "Improve" instead of "Improving" or "Improved").  Again, not everyone 
follows it, but it is good to keep it consistent, right?

@NoQ knock-knock!




Comment at: clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp:199
   // Utility methods
-  std::pair
-  static assumeZero(CheckerContext &C,
-ProgramStateRef state, SVal V, QualType Ty);
+  std::pair static assumeZero(
+  ProgramStateRef state, SVal V);

Can you please put `static` before return type, it will be more consistent with 
other static methods nearby.



Comment at: clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp:283-288
+  auto states = std::make_pair(state, state);
+
   Optional val = V.getAs();
-  if (!val)
-return std::pair(state, state);
+  // FIXME: We should understand how LazyCompoundVal can be possible here,
+  // fix the root cause and get rid of this check.
+  if (val && !V.getAs())

I know that other methods here don't name variables according to llvm-style 
guide.  Analyzer's code is one of the least complying areas of the whole LLVM 
project.  However, I suggest not to make it worse and capitalize all parameter 
and local variable names.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77062/new/

https://reviews.llvm.org/D77062



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D83877: [Analyzer] Changed in SmartPtrModeling to handle Swap

2020-07-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

The same thing with the commit message here, it would be better as just "Change"


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83877/new/

https://reviews.llvm.org/D83877



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D83942: [analyzer][tests] Add a notion of project sizes

2020-07-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, xazax.hun, Szelethus, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware.
Herald added a project: clang.

Whith the number of projects growing, it is important to be able to
filter them in a more convenient way than by names.  It is especially
important for benchmarks, when it is not viable to analyze big
projects 20 or 50 times in a row.

Because of this reason, this commit adds a notion of sizes and a
filtering interface that puts a limit on a maximum size of the project
to analyze or benchmark.

Sizes assigned to the projects in this commit, do not directly
correspond to the number of lines or files in the project.  The key
factor that is important for the developers of the analyzer is the
time it takes to analyze the project.  And for this very reason,
"size" basically helps to cluster projects based on their analysis
time.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D83942

Files:
  clang/utils/analyzer/ProjectMap.py
  clang/utils/analyzer/SATest.py
  clang/utils/analyzer/projects/projects.json

Index: clang/utils/analyzer/projects/projects.json
===
--- clang/utils/analyzer/projects/projects.json
+++ clang/utils/analyzer/projects/projects.json
@@ -4,139 +4,159 @@
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/jarro2783/cxxopts.git";,
-"commit": "794c975"
+"commit": "794c975",
+"size": "tiny"
   },
   {
 "name": "box2d",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/erincatto/box2d.git";,
-"commit": "1025f9a"
+"commit": "1025f9a",
+"size": "small"
   },
   {
 "name": "tinyexpr",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/codeplea/tinyexpr.git";,
-"commit": "ffb0d41"
+"commit": "ffb0d41",
+"size": "tiny"
   },
   {
 "name": "symengine",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/symengine/symengine.git";,
-"commit": "4f669d59"
+"commit": "4f669d59",
+"size": "small"
   },
   {
 "name": "termbox",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/nsf/termbox.git";,
-"commit": "0df1355"
+"commit": "0df1355",
+"size": "tiny"
   },
   {
 "name": "tinyvm",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/jakogut/tinyvm.git";,
-"commit": "10c25d8"
+"commit": "10c25d8",
+"size": "tiny"
   },
   {
 "name": "tinyspline",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/msteinbeck/tinyspline.git";,
-"commit": "f8b1ab7"
+"commit": "f8b1ab7",
+"size": "tiny"
   },
   {
 "name": "oatpp",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/oatpp/oatpp.git";,
-"commit": "d3e60fb"
+"commit": "d3e60fb",
+"size": "small"
   },
   {
 "name": "libsoundio",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/andrewrk/libsoundio.git";,
-"commit": "b810bf2"
+"commit": "b810bf2",
+"size": "tiny"
   },
   {
 "name": "zstd",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/facebook/zstd.git";,
-"commit": "2af4e073"
+"commit": "2af4e073",
+"size": "small"
   },
   {
 "name": "simbody",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/simbody/simbody.git";,
-"commit": "5cf513d"
+"commit": "5cf513d",
+"size": "big"
   },
   {
 "name": "duckdb",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/cwida/duckdb.git";,
-"commit": "d098c9f"
+"commit": "d098c9f",
+"size": "big"
   },
   {
 "name": "drogon",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/an-tao/drogon.git";,
-"commit": "fd2a612"
+"commit": "fd2a612",
+"size": "small"
   },
   {
 "name": "fmt",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/fmtlib/fmt.git";,
-"commit": "5e7c70e"
+"commit": "5e7c70e",
+"size": "small"
   },
   {
 "name": "re2",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/google/re2.git";,
-"commit": "2b25567"
+"commit": "2b25567",
+"size": "small"
   },
   {
 "name": "cppcheck",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/danmar/cppcheck.git";,
-"commit": "5fa3d53"
+"commit": "5fa3d53",
+"size": "small"
   },
   {
 "name": "harfbuzz",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/harfbuzz/harfbuzz.git";,
-"commit": "f8d345e"
+"commit": "f8d345e",
+"size": "small"
   },
   {
 "name": "capnproto",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/capnproto/capnproto.git";,
-"commit": 

[PATCH] D82381: [analyzer] Introduce small improvements to the solver infra

2020-07-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 278454.
vsavchenko added a comment.

Rename function


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82381/new/

https://reviews.llvm.org/D82381

Files:
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -89,7 +89,7 @@
   }
 
   TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
- BinaryOperatorKind QueriedOP) const {
+ BinaryOperatorKind QueriedOP) const {
 return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
   }
 
@@ -364,6 +364,18 @@
   return newRanges;
 }
 
+RangeSet RangeSet::Delete(BasicValueFactory &BV, Factory &F,
+  const llvm::APSInt &Point) const {
+  llvm::APSInt Upper = Point;
+  llvm::APSInt Lower = Point;
+
+  ++Upper;
+  --Lower;
+
+  // Notice that the lower bound is greater than the upper bound.
+  return Intersect(BV, F, Upper, Lower);
+}
+
 void RangeSet::print(raw_ostream &os) const {
   bool isFirst = true;
   os << "{ ";
@@ -381,6 +393,107 @@
 
 namespace {
 
+//===--===//
+//Intersection functions
+//===--===//
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
+
+template  struct IntersectionTraits;
+
+template  struct IntersectionTraits {
+  // Found RangeSet, no need to check any further
+  using Type = RangeSet;
+};
+
+template <> struct IntersectionTraits<> {
+  // We ran out of types, and we didn't find any RangeSet, so the result should
+  // be optional.
+  using Type = Optional;
+};
+
+template 
+struct IntersectionTraits {
+  // If current type is Optional or a raw pointer, we should keep looking.
+  using Type = typename IntersectionTraits::Type;
+};
+
+template 
+LLVM_NODISCARD inline EndTy intersect(BasicValueFactory &BV,
+  RangeSet::Factory &F, EndTy End) {
+  // If the list contains only RangeSet or Optional, simply return
+  // that range set.
+  return End;
+}
+
+LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional
+intersect(BasicValueFactory &BV, RangeSet::Factory &F, const RangeSet *End) {
+  // This is an extraneous conversion from a raw pointer into Optional
+  if (End) {
+return *End;
+  }
+  return llvm::None;
+}
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
+  // Here we call either the  or  version
+  // of the function and can be sure that the result is RangeSet.
+  return intersect(BV, F, Head.Intersect(BV, F, Second), Tail...);
+}
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
+  if (Second) {
+// Here we call the  version of the function...
+return intersect(BV, F, Head, *Second, Tail...);
+  }
+  // ...and here it is either  or , which
+  // means that the result is definitely RangeSet.
+  return intersect(BV, F, Head, Tail...);
+}
+
+/// Main generic intersect function.
+/// It intersects all of the given range sets.  If some of the given arguments
+/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+///
+/// Available representations for the arguments are:
+///   * RangeSet
+///   * Optional
+///   * RangeSet *
+/// Pointer to a RangeSet is automatically assumed to be nullable and will get
+/// checked as well as the optional version.  If this behaviour is undesired,
+/// please dereference the pointer in the call.
+///
+/// Return type depends on the arguments' types.  If we can be sure in compile
+/// time that there will be a range set as a result, the returning type is
+/// simply RangeSet, in other cases we have to back off to Optional.
+///
+/// Please, prefer optional range sets to raw pointers.  If the last argument is
+/// a raw pointer and all previous arguments are None, it will cost one
+/// additional check to convert RangeSet * into Optional.
+template 
+LLVM_NODISCARD inline
+typename IntersectionTraits::Type
+intersect(BasicValueFactory &BV, RangeSet::Factory &F, HeadTy Head,
+  SecondTy Second, 

[PATCH] D83942: [analyzer][tests] Add a notion of project sizes

2020-07-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked an inline comment as done.
vsavchenko added a comment.

In D83942#2155762 , @Szelethus wrote:

> I don't speak snek, but I approve this message!


Thanks 😊

> I suppose LLVM could be a HUGE project?

Yurp, LLVM, pytorch, (surprisingly!) CMake




Comment at: clang/utils/analyzer/ProjectMap.py:31
+SMALL: 1min-10min
+BIG:   10min-1h
+HUGE:  >1h

Szelethus wrote:
> Just an observation rather then a suggestion, its interesting that we don't 
> have a MEDIUM size in between SMALL and BIG. I think the TINY category 
> describes the sub-minute runs well, and it'd be awkward to introduce a 
> project in between SMALL and BIG, so I don't immediately see a time interval 
> we need to categorize.
Yeah, maybe it's good to add MEDIUM with 10min-30min


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83942/new/

https://reviews.llvm.org/D83942



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D83942: [analyzer][tests] Add a notion of project sizes

2020-07-20 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

In D83942#2156320 , @NoQ wrote:

> > Sizes assigned to the projects in this commit, do not directly
> >  correspond to the number of lines or files in the project.
>
> Maybe `QUICK`/`NORMAL`/`SLOW` then? Or by purpose: 
> `BENCHMARK`/`DAILY`/`PARANOID`?


I am not against the idea of naming it differently, but then we need to get all 
whereabouts vocabulary straight.  What is the noun? Speed?  And what should be 
the command line option?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83942/new/

https://reviews.llvm.org/D83942



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D83942: [analyzer][tests] Add a notion of project sizes

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 279738.
vsavchenko added a comment.

Make --projects and --max-size compatible


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83942/new/

https://reviews.llvm.org/D83942

Files:
  clang/utils/analyzer/ProjectMap.py
  clang/utils/analyzer/SATest.py
  clang/utils/analyzer/projects/projects.json

Index: clang/utils/analyzer/projects/projects.json
===
--- clang/utils/analyzer/projects/projects.json
+++ clang/utils/analyzer/projects/projects.json
@@ -4,139 +4,159 @@
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/jarro2783/cxxopts.git";,
-"commit": "794c975"
+"commit": "794c975",
+"size": "tiny"
   },
   {
 "name": "box2d",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/erincatto/box2d.git";,
-"commit": "1025f9a"
+"commit": "1025f9a",
+"size": "small"
   },
   {
 "name": "tinyexpr",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/codeplea/tinyexpr.git";,
-"commit": "ffb0d41"
+"commit": "ffb0d41",
+"size": "tiny"
   },
   {
 "name": "symengine",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/symengine/symengine.git";,
-"commit": "4f669d59"
+"commit": "4f669d59",
+"size": "small"
   },
   {
 "name": "termbox",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/nsf/termbox.git";,
-"commit": "0df1355"
+"commit": "0df1355",
+"size": "tiny"
   },
   {
 "name": "tinyvm",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/jakogut/tinyvm.git";,
-"commit": "10c25d8"
+"commit": "10c25d8",
+"size": "tiny"
   },
   {
 "name": "tinyspline",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/msteinbeck/tinyspline.git";,
-"commit": "f8b1ab7"
+"commit": "f8b1ab7",
+"size": "tiny"
   },
   {
 "name": "oatpp",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/oatpp/oatpp.git";,
-"commit": "d3e60fb"
+"commit": "d3e60fb",
+"size": "small"
   },
   {
 "name": "libsoundio",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/andrewrk/libsoundio.git";,
-"commit": "b810bf2"
+"commit": "b810bf2",
+"size": "tiny"
   },
   {
 "name": "zstd",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/facebook/zstd.git";,
-"commit": "2af4e073"
+"commit": "2af4e073",
+"size": "small"
   },
   {
 "name": "simbody",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/simbody/simbody.git";,
-"commit": "5cf513d"
+"commit": "5cf513d",
+"size": "big"
   },
   {
 "name": "duckdb",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/cwida/duckdb.git";,
-"commit": "d098c9f"
+"commit": "d098c9f",
+"size": "big"
   },
   {
 "name": "drogon",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/an-tao/drogon.git";,
-"commit": "fd2a612"
+"commit": "fd2a612",
+"size": "small"
   },
   {
 "name": "fmt",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/fmtlib/fmt.git";,
-"commit": "5e7c70e"
+"commit": "5e7c70e",
+"size": "small"
   },
   {
 "name": "re2",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/google/re2.git";,
-"commit": "2b25567"
+"commit": "2b25567",
+"size": "small"
   },
   {
 "name": "cppcheck",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/danmar/cppcheck.git";,
-"commit": "5fa3d53"
+"commit": "5fa3d53",
+"size": "small"
   },
   {
 "name": "harfbuzz",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/harfbuzz/harfbuzz.git";,
-"commit": "f8d345e"
+"commit": "f8d345e",
+"size": "small"
   },
   {
 "name": "capnproto",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/capnproto/capnproto.git";,
-"commit": "8be1c9f"
+"commit": "8be1c9f",
+"size": "small"
   },
   {
 "name": "tmux",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/tmux/tmux.git";,
-"commit": "a5f99e1"
+"commit": "a5f99e1",
+"size": "big"
   },
   {
 "name": "faiss",
 "mode": 1,
 "source": "git",
 "origin": "https://github.com/facebookresearch/faiss.git";,
-"commit": "9e5d5b7"
+"commit": "9e5d5b7",
+"size": "small"
   }
 ]
Index: clang/utils/analyzer/SATest.py
===
--- clang/utils/analyzer/SATest.py
+++ clang/utils/analyzer/SATest.py
@@ -37,7 +37,7 @@
 
 SATestBuild.VERBOSE = args.verbose
 
-projects = get_projects(parser, args.projects)
+projects = get_projects(parser, args)
 tester = SATestBuild.Regress

[Differential] D82381: [analyzer] Introduce small improvements to the solver infra

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGf531c1c7c0d5: [analyzer] Introduce small improvements to the 
solver infra (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82381/new/

https://reviews.llvm.org/D82381

Files:
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -89,7 +89,7 @@
   }
 
   TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
- BinaryOperatorKind QueriedOP) const {
+ BinaryOperatorKind QueriedOP) const {
 return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
   }
 
@@ -364,6 +364,18 @@
   return newRanges;
 }
 
+RangeSet RangeSet::Delete(BasicValueFactory &BV, Factory &F,
+  const llvm::APSInt &Point) const {
+  llvm::APSInt Upper = Point;
+  llvm::APSInt Lower = Point;
+
+  ++Upper;
+  --Lower;
+
+  // Notice that the lower bound is greater than the upper bound.
+  return Intersect(BV, F, Upper, Lower);
+}
+
 void RangeSet::print(raw_ostream &os) const {
   bool isFirst = true;
   os << "{ ";
@@ -381,6 +393,107 @@
 
 namespace {
 
+//===--===//
+//Intersection functions
+//===--===//
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
+
+template  struct IntersectionTraits;
+
+template  struct IntersectionTraits {
+  // Found RangeSet, no need to check any further
+  using Type = RangeSet;
+};
+
+template <> struct IntersectionTraits<> {
+  // We ran out of types, and we didn't find any RangeSet, so the result should
+  // be optional.
+  using Type = Optional;
+};
+
+template 
+struct IntersectionTraits {
+  // If current type is Optional or a raw pointer, we should keep looking.
+  using Type = typename IntersectionTraits::Type;
+};
+
+template 
+LLVM_NODISCARD inline EndTy intersect(BasicValueFactory &BV,
+  RangeSet::Factory &F, EndTy End) {
+  // If the list contains only RangeSet or Optional, simply return
+  // that range set.
+  return End;
+}
+
+LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional
+intersect(BasicValueFactory &BV, RangeSet::Factory &F, const RangeSet *End) {
+  // This is an extraneous conversion from a raw pointer into Optional
+  if (End) {
+return *End;
+  }
+  return llvm::None;
+}
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
+  // Here we call either the  or  version
+  // of the function and can be sure that the result is RangeSet.
+  return intersect(BV, F, Head.Intersect(BV, F, Second), Tail...);
+}
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
+  if (Second) {
+// Here we call the  version of the function...
+return intersect(BV, F, Head, *Second, Tail...);
+  }
+  // ...and here it is either  or , which
+  // means that the result is definitely RangeSet.
+  return intersect(BV, F, Head, Tail...);
+}
+
+/// Main generic intersect function.
+/// It intersects all of the given range sets.  If some of the given arguments
+/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+///
+/// Available representations for the arguments are:
+///   * RangeSet
+///   * Optional
+///   * RangeSet *
+/// Pointer to a RangeSet is automatically assumed to be nullable and will get
+/// checked as well as the optional version.  If this behaviour is undesired,
+/// please dereference the pointer in the call.
+///
+/// Return type depends on the arguments' types.  If we can be sure in compile
+/// time that there will be a range set as a result, the returning type is
+/// simply RangeSet, in other cases we have to back off to Optional.
+///
+/// Please, prefer optional range sets to raw pointers.  If the last argument is
+/// a raw pointer and all previous arguments are None, it will cost one
+/// additional check to convert RangeSet * into Optional.
+template 
+LLVM_NODISC

[PATCH] D82381: [analyzer] Introduce small improvements to the solver infra

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGf531c1c7c0d5: [analyzer] Introduce small improvements to the 
solver infra (authored by vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82381/new/

https://reviews.llvm.org/D82381

Files:
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -89,7 +89,7 @@
   }
 
   TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
- BinaryOperatorKind QueriedOP) const {
+ BinaryOperatorKind QueriedOP) const {
 return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
   }
 
@@ -364,6 +364,18 @@
   return newRanges;
 }
 
+RangeSet RangeSet::Delete(BasicValueFactory &BV, Factory &F,
+  const llvm::APSInt &Point) const {
+  llvm::APSInt Upper = Point;
+  llvm::APSInt Lower = Point;
+
+  ++Upper;
+  --Lower;
+
+  // Notice that the lower bound is greater than the upper bound.
+  return Intersect(BV, F, Upper, Lower);
+}
+
 void RangeSet::print(raw_ostream &os) const {
   bool isFirst = true;
   os << "{ ";
@@ -381,6 +393,107 @@
 
 namespace {
 
+//===--===//
+//Intersection functions
+//===--===//
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
+
+template  struct IntersectionTraits;
+
+template  struct IntersectionTraits {
+  // Found RangeSet, no need to check any further
+  using Type = RangeSet;
+};
+
+template <> struct IntersectionTraits<> {
+  // We ran out of types, and we didn't find any RangeSet, so the result should
+  // be optional.
+  using Type = Optional;
+};
+
+template 
+struct IntersectionTraits {
+  // If current type is Optional or a raw pointer, we should keep looking.
+  using Type = typename IntersectionTraits::Type;
+};
+
+template 
+LLVM_NODISCARD inline EndTy intersect(BasicValueFactory &BV,
+  RangeSet::Factory &F, EndTy End) {
+  // If the list contains only RangeSet or Optional, simply return
+  // that range set.
+  return End;
+}
+
+LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional
+intersect(BasicValueFactory &BV, RangeSet::Factory &F, const RangeSet *End) {
+  // This is an extraneous conversion from a raw pointer into Optional
+  if (End) {
+return *End;
+  }
+  return llvm::None;
+}
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
+  // Here we call either the  or  version
+  // of the function and can be sure that the result is RangeSet.
+  return intersect(BV, F, Head.Intersect(BV, F, Second), Tail...);
+}
+
+template 
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
+  if (Second) {
+// Here we call the  version of the function...
+return intersect(BV, F, Head, *Second, Tail...);
+  }
+  // ...and here it is either  or , which
+  // means that the result is definitely RangeSet.
+  return intersect(BV, F, Head, Tail...);
+}
+
+/// Main generic intersect function.
+/// It intersects all of the given range sets.  If some of the given arguments
+/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+///
+/// Available representations for the arguments are:
+///   * RangeSet
+///   * Optional
+///   * RangeSet *
+/// Pointer to a RangeSet is automatically assumed to be nullable and will get
+/// checked as well as the optional version.  If this behaviour is undesired,
+/// please dereference the pointer in the call.
+///
+/// Return type depends on the arguments' types.  If we can be sure in compile
+/// time that there will be a range set as a result, the returning type is
+/// simply RangeSet, in other cases we have to back off to Optional.
+///
+/// Please, prefer optional range sets to raw pointers.  If the last argument is
+/// a raw pointer and all previous arguments are None, it will cost one
+/// additional check to convert RangeSet * into Optional.
+template 
+LLVM_NODISCARD inline
+typename IntersectionTraits::Type
+i

[PATCH] D82445: [analyzer][solver] Track symbol equivalence

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb13d9878b8dc: [analyzer][solver] Track symbol equivalence 
(authored by vsavchenko).

Changed prior to commit:
  https://reviews.llvm.org/D82445?vs=275981&id=279746#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82445/new/

https://reviews.llvm.org/D82445

Files:
  clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
  clang/test/Analysis/equality_tracking.c

Index: clang/test/Analysis/equality_tracking.c
===
--- /dev/null
+++ clang/test/Analysis/equality_tracking.c
@@ -0,0 +1,167 @@
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -analyzer-config eagerly-assume=false
+
+#define NULL (void *)0
+
+#define UCHAR_MAX (unsigned char)(~0U)
+#define CHAR_MAX (char)(UCHAR_MAX & (UCHAR_MAX >> 1))
+#define CHAR_MIN (char)(UCHAR_MAX & ~(UCHAR_MAX >> 1))
+
+void clang_analyzer_eval(int);
+void clang_analyzer_warnIfReached();
+
+int getInt();
+
+void zeroImpliesEquality(int a, int b) {
+  clang_analyzer_eval((a - b) == 0); // expected-warning{{UNKNOWN}}
+  if ((a - b) == 0) {
+clang_analyzer_eval(b != a);// expected-warning{{FALSE}}
+clang_analyzer_eval(b == a);// expected-warning{{TRUE}}
+clang_analyzer_eval(!(a != b)); // expected-warning{{TRUE}}
+clang_analyzer_eval(!(b == a)); // expected-warning{{FALSE}}
+return;
+  }
+  clang_analyzer_eval((a - b) == 0); // expected-warning{{FALSE}}
+  // FIXME: we should track disequality information as well
+  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+}
+
+void zeroImpliesReversedEqual(int a, int b) {
+  clang_analyzer_eval((b - a) == 0); // expected-warning{{UNKNOWN}}
+  if ((b - a) == 0) {
+clang_analyzer_eval(b != a); // expected-warning{{FALSE}}
+clang_analyzer_eval(b == a); // expected-warning{{TRUE}}
+return;
+  }
+  clang_analyzer_eval((b - a) == 0); // expected-warning{{FALSE}}
+  // FIXME: we should track disequality information as well
+  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+}
+
+void canonicalEqual(int a, int b) {
+  clang_analyzer_eval(a == b); // expected-warning{{UNKNOWN}}
+  if (a == b) {
+clang_analyzer_eval(b == a); // expected-warning{{TRUE}}
+return;
+  }
+  clang_analyzer_eval(a == b); // expected-warning{{FALSE}}
+  clang_analyzer_eval(b == a); // expected-warning{{FALSE}}
+}
+
+void test(int a, int b, int c, int d) {
+  if (a == b && c == d) {
+if (a == 0 && b == d) {
+  clang_analyzer_eval(c == 0); // expected-warning{{TRUE}}
+}
+c = 10;
+if (b == d) {
+  clang_analyzer_eval(c == 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(d == 10); // expected-warning{{UNKNOWN}}
+// expected-warning@-1{{FALSE}}
+  clang_analyzer_eval(b == a);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(a == d);  // expected-warning{{TRUE}}
+
+  b = getInt();
+  clang_analyzer_eval(a == d); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a == b); // expected-warning{{UNKNOWN}}
+}
+  }
+
+  if (a != b && b == c) {
+if (c == 42) {
+  clang_analyzer_eval(b == 42); // expected-warning{{TRUE}}
+  // FIXME: we should track disequality information as well
+  clang_analyzer_eval(a != 42); // expected-warning{{UNKNOWN}}
+}
+  }
+}
+
+void testIntersection(int a, int b, int c) {
+  if (a < 42 && b > 15 && c >= 25 && c <= 30) {
+if (a != b)
+  return;
+
+clang_analyzer_eval(a > 15);  // expected-warning{{TRUE}}
+clang_analyzer_eval(b < 42);  // expected-warning{{TRUE}}
+clang_analyzer_eval(a <= 30); // expected-warning{{UNKNOWN}}
+
+if (c == b) {
+  // For all equal symbols, we should track the minimal common range.
+  //
+  // Also, it should be noted that c is dead at this point, but the
+  // constraint initially associated with c is still around.
+  clang_analyzer_eval(a >= 25 && a <= 30); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b >= 25 && b <= 30); // expected-warning{{TRUE}}
+}
+  }
+}
+
+void testPromotion(int a, char b) {
+  if (b > 10) {
+if (a == b) {
+  // FIXME: support transferring char ranges onto equal int symbols
+  //when char is promoted to int
+  clang_analyzer_eval(a > 10);// expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a <= CHAR_MAX); // expected-warning{{UNKNOWN}}
+}
+  }
+}
+
+void testProm

[Differential] D83286: [analyzer][solver] Track symbol disequalities

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGe63b488f2755: [analyzer][solver] Track symbol disequalities 
(authored by vsavchenko).

Changed prior to commit:
  https://reviews.llvm.org/D83286?vs=276729&id=279082#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83286/new/

https://reviews.llvm.org/D83286

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/equality_tracking.c
  clang/test/Analysis/mutually_exclusive_null_fp.cpp

Index: clang/test/Analysis/mutually_exclusive_null_fp.cpp
===
--- /dev/null
+++ clang/test/Analysis/mutually_exclusive_null_fp.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/56586853
+// expected-no-diagnostics
+
+struct Data {
+  int x;
+  Data *data;
+};
+
+int compare(Data &a, Data &b) {
+  Data *aData = a.data;
+  Data *bData = b.data;
+
+  // Covers the cases where both pointers are null as well as both pointing to the same buffer.
+  if (aData == bData)
+return 0;
+
+  if (aData && !bData)
+return 1;
+
+  if (!aData && bData)
+return -1;
+
+  return compare(*aData, *bData); // no-warning
+}
Index: clang/test/Analysis/equality_tracking.c
===
--- clang/test/Analysis/equality_tracking.c
+++ clang/test/Analysis/equality_tracking.c
@@ -23,9 +23,8 @@
 return;
   }
   clang_analyzer_eval((a - b) == 0); // expected-warning{{FALSE}}
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b == a);   // expected-warning{{FALSE}}
+  clang_analyzer_eval(b != a);   // expected-warning{{TRUE}}
 }
 
 void zeroImpliesReversedEqual(int a, int b) {
@@ -36,9 +35,8 @@
 return;
   }
   clang_analyzer_eval((b - a) == 0); // expected-warning{{FALSE}}
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b == a);   // expected-warning{{FALSE}}
+  clang_analyzer_eval(b != a);   // expected-warning{{TRUE}}
 }
 
 void canonicalEqual(int a, int b) {
@@ -73,8 +71,7 @@
   if (a != b && b == c) {
 if (c == 42) {
   clang_analyzer_eval(b == 42); // expected-warning{{TRUE}}
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(a != 42); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a != 42); // expected-warning{{TRUE}}
 }
   }
 }
@@ -144,8 +141,31 @@
 
   if (a != b && b == c) {
 if (c == NULL) {
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(a != NULL); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a != NULL); // expected-warning{{TRUE}}
+}
+  }
+}
+
+void testDisequalitiesAfter(int a, int b, int c) {
+  if (a >= 10 && b <= 42) {
+if (a == b && c == 15 && c != a) {
+  clang_analyzer_eval(b != c);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(a != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b >= 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a <= 42); // expected-warning{{TRUE}}
+}
+  }
+}
+
+void testDisequalitiesBefore(int a, int b, int c) {
+  if (a >= 10 && b <= 42 && c == 15) {
+if (a == b && c != a) {
+  clang_analyzer_eval(b != c);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(a != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b >= 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a <= 42); // expected-warning{{TRUE}}
 }
   }
 }
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -401,6 +401,9 @@
 REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
 REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
 
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass)
+REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet)
+
 namespace {
 /// This class encapsulates a set of symbols equal to each other.
 ///
@@ -450,6 +453,26 @@
   LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
  SymbolReaper &Reaper);
 
+  LLVM_NODISCARD static inline ProgramStateRef
+  markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ 

[Differential] D82445: [analyzer][solver] Track symbol equivalence

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb13d9878b8dc: [analyzer][solver] Track symbol equivalence 
(authored by vsavchenko).

Changed prior to commit:
  https://reviews.llvm.org/D82445?vs=275981&id=279081#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D82445/new/

https://reviews.llvm.org/D82445

Files:
  clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
  
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
  clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
  clang/test/Analysis/equality_tracking.c

Index: clang/test/Analysis/equality_tracking.c
===
--- /dev/null
+++ clang/test/Analysis/equality_tracking.c
@@ -0,0 +1,167 @@
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -analyzer-config eagerly-assume=false
+
+#define NULL (void *)0
+
+#define UCHAR_MAX (unsigned char)(~0U)
+#define CHAR_MAX (char)(UCHAR_MAX & (UCHAR_MAX >> 1))
+#define CHAR_MIN (char)(UCHAR_MAX & ~(UCHAR_MAX >> 1))
+
+void clang_analyzer_eval(int);
+void clang_analyzer_warnIfReached();
+
+int getInt();
+
+void zeroImpliesEquality(int a, int b) {
+  clang_analyzer_eval((a - b) == 0); // expected-warning{{UNKNOWN}}
+  if ((a - b) == 0) {
+clang_analyzer_eval(b != a);// expected-warning{{FALSE}}
+clang_analyzer_eval(b == a);// expected-warning{{TRUE}}
+clang_analyzer_eval(!(a != b)); // expected-warning{{TRUE}}
+clang_analyzer_eval(!(b == a)); // expected-warning{{FALSE}}
+return;
+  }
+  clang_analyzer_eval((a - b) == 0); // expected-warning{{FALSE}}
+  // FIXME: we should track disequality information as well
+  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+}
+
+void zeroImpliesReversedEqual(int a, int b) {
+  clang_analyzer_eval((b - a) == 0); // expected-warning{{UNKNOWN}}
+  if ((b - a) == 0) {
+clang_analyzer_eval(b != a); // expected-warning{{FALSE}}
+clang_analyzer_eval(b == a); // expected-warning{{TRUE}}
+return;
+  }
+  clang_analyzer_eval((b - a) == 0); // expected-warning{{FALSE}}
+  // FIXME: we should track disequality information as well
+  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+}
+
+void canonicalEqual(int a, int b) {
+  clang_analyzer_eval(a == b); // expected-warning{{UNKNOWN}}
+  if (a == b) {
+clang_analyzer_eval(b == a); // expected-warning{{TRUE}}
+return;
+  }
+  clang_analyzer_eval(a == b); // expected-warning{{FALSE}}
+  clang_analyzer_eval(b == a); // expected-warning{{FALSE}}
+}
+
+void test(int a, int b, int c, int d) {
+  if (a == b && c == d) {
+if (a == 0 && b == d) {
+  clang_analyzer_eval(c == 0); // expected-warning{{TRUE}}
+}
+c = 10;
+if (b == d) {
+  clang_analyzer_eval(c == 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(d == 10); // expected-warning{{UNKNOWN}}
+// expected-warning@-1{{FALSE}}
+  clang_analyzer_eval(b == a);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(a == d);  // expected-warning{{TRUE}}
+
+  b = getInt();
+  clang_analyzer_eval(a == d); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a == b); // expected-warning{{UNKNOWN}}
+}
+  }
+
+  if (a != b && b == c) {
+if (c == 42) {
+  clang_analyzer_eval(b == 42); // expected-warning{{TRUE}}
+  // FIXME: we should track disequality information as well
+  clang_analyzer_eval(a != 42); // expected-warning{{UNKNOWN}}
+}
+  }
+}
+
+void testIntersection(int a, int b, int c) {
+  if (a < 42 && b > 15 && c >= 25 && c <= 30) {
+if (a != b)
+  return;
+
+clang_analyzer_eval(a > 15);  // expected-warning{{TRUE}}
+clang_analyzer_eval(b < 42);  // expected-warning{{TRUE}}
+clang_analyzer_eval(a <= 30); // expected-warning{{UNKNOWN}}
+
+if (c == b) {
+  // For all equal symbols, we should track the minimal common range.
+  //
+  // Also, it should be noted that c is dead at this point, but the
+  // constraint initially associated with c is still around.
+  clang_analyzer_eval(a >= 25 && a <= 30); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b >= 25 && b <= 30); // expected-warning{{TRUE}}
+}
+  }
+}
+
+void testPromotion(int a, char b) {
+  if (b > 10) {
+if (a == b) {
+  // FIXME: support transferring char ranges onto equal int symbols
+  //when char is promoted to int
+  clang_analyzer_eval(a > 10);// expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a <= CHAR_MAX); // expected-warning{{UNKNOWN}}
+}
+  }
+}
+
+void testProm

[PATCH] D83286: [analyzer][solver] Track symbol disequalities

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGe63b488f2755: [analyzer][solver] Track symbol disequalities 
(authored by vsavchenko).

Changed prior to commit:
  https://reviews.llvm.org/D83286?vs=276729&id=279747#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83286/new/

https://reviews.llvm.org/D83286

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/equality_tracking.c
  clang/test/Analysis/mutually_exclusive_null_fp.cpp

Index: clang/test/Analysis/mutually_exclusive_null_fp.cpp
===
--- /dev/null
+++ clang/test/Analysis/mutually_exclusive_null_fp.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// rdar://problem/56586853
+// expected-no-diagnostics
+
+struct Data {
+  int x;
+  Data *data;
+};
+
+int compare(Data &a, Data &b) {
+  Data *aData = a.data;
+  Data *bData = b.data;
+
+  // Covers the cases where both pointers are null as well as both pointing to the same buffer.
+  if (aData == bData)
+return 0;
+
+  if (aData && !bData)
+return 1;
+
+  if (!aData && bData)
+return -1;
+
+  return compare(*aData, *bData); // no-warning
+}
Index: clang/test/Analysis/equality_tracking.c
===
--- clang/test/Analysis/equality_tracking.c
+++ clang/test/Analysis/equality_tracking.c
@@ -23,9 +23,8 @@
 return;
   }
   clang_analyzer_eval((a - b) == 0); // expected-warning{{FALSE}}
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b == a);   // expected-warning{{FALSE}}
+  clang_analyzer_eval(b != a);   // expected-warning{{TRUE}}
 }
 
 void zeroImpliesReversedEqual(int a, int b) {
@@ -36,9 +35,8 @@
 return;
   }
   clang_analyzer_eval((b - a) == 0); // expected-warning{{FALSE}}
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(b == a);   // expected-warning{{FALSE}}
+  clang_analyzer_eval(b != a);   // expected-warning{{TRUE}}
 }
 
 void canonicalEqual(int a, int b) {
@@ -73,8 +71,7 @@
   if (a != b && b == c) {
 if (c == 42) {
   clang_analyzer_eval(b == 42); // expected-warning{{TRUE}}
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(a != 42); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a != 42); // expected-warning{{TRUE}}
 }
   }
 }
@@ -144,8 +141,31 @@
 
   if (a != b && b == c) {
 if (c == NULL) {
-  // FIXME: we should track disequality information as well
-  clang_analyzer_eval(a != NULL); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a != NULL); // expected-warning{{TRUE}}
+}
+  }
+}
+
+void testDisequalitiesAfter(int a, int b, int c) {
+  if (a >= 10 && b <= 42) {
+if (a == b && c == 15 && c != a) {
+  clang_analyzer_eval(b != c);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(a != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b >= 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a <= 42); // expected-warning{{TRUE}}
+}
+  }
+}
+
+void testDisequalitiesBefore(int a, int b, int c) {
+  if (a >= 10 && b <= 42 && c == 15) {
+if (a == b && c != a) {
+  clang_analyzer_eval(b != c);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(a != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b != 15); // expected-warning{{TRUE}}
+  clang_analyzer_eval(b >= 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a <= 42); // expected-warning{{TRUE}}
 }
   }
 }
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -401,6 +401,9 @@
 REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
 REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
 
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass)
+REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet)
+
 namespace {
 /// This class encapsulates a set of symbols equal to each other.
 ///
@@ -450,6 +453,26 @@
   LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
  SymbolReaper &Reaper);
 
+  LLVM_NODISCARD static inline ProgramStateRef
+  markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+   ProgramStateRef State, SymbolRef First, SymbolRef 

[PATCH] D84303: [analyzer][tests] Fix SATest update functionality

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Not all projects in the project map file might have newer results
for updating, we should handle this situation gracefully.

Additionally, not every user of the test system would want storing
reference results in git.  For this reason, git functionality is now
optional.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D84303

Files:
  clang/utils/analyzer/SATest.py
  clang/utils/analyzer/SATestUpdateDiffs.py


Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -15,7 +15,7 @@
 Verbose = 0
 
 
-def update_reference_results(project: ProjectInfo):
+def update_reference_results(project: ProjectInfo, git: bool = False):
 test_info = SATestBuild.TestInfo(project)
 tester = SATestBuild.ProjectTester(test_info)
 project_dir = tester.get_project_dir()
@@ -27,9 +27,10 @@
 created_results_path = tester.get_output_dir()
 
 if not os.path.exists(created_results_path):
-print("New results not found, was SATestBuild.py previously run?",
+print(f"Skipping project '{project.name}', "
+  f"it doesn't have newer results.",
   file=sys.stderr)
-sys.exit(1)
+return
 
 build_log_path = SATestBuild.get_build_log_path(ref_results_path)
 build_log_dir = os.path.dirname(os.path.abspath(build_log_path))
@@ -45,7 +46,8 @@
 # Remove reference results: in git, and then again for a good measure
 # with rm, as git might not remove things fully if there are empty
 # directories involved.
-run_cmd(f"git rm -r -q '{ref_results_path}'")
+if git:
+run_cmd(f"git rm -r -q '{ref_results_path}'")
 shutil.rmtree(ref_results_path)
 
 # Replace reference results with a freshly computed once.
@@ -60,22 +62,11 @@
 # Clean up the generated difference results.
 SATestBuild.cleanup_reference_results(ref_results_path)
 
-run_cmd(f"git add '{ref_results_path}'")
+if git:
+run_cmd(f"git add '{ref_results_path}'")
 
 
-# TODO: use argparse
-def main(argv):
-if len(argv) == 2 and argv[1] in ("-h", "--help"):
-print("Update static analyzer reference results based "
-  "\non the previous run of SATestBuild.py.\n"
-  "\nN.B.: Assumes that SATestBuild.py was just run",
-  file=sys.stderr)
-sys.exit(1)
-
-project_map = ProjectMap()
-for project in project_map.projects:
-update_reference_results(project)
-
-
-if __name__ == '__main__':
-main(sys.argv)
+if __name__ == "__main__":
+print("SATestUpdateDiffs.py should not be used on its own.")
+print("Please use 'SATest.py update' instead")
+sys.exit(1)
Index: clang/utils/analyzer/SATest.py
===
--- clang/utils/analyzer/SATest.py
+++ clang/utils/analyzer/SATest.py
@@ -78,7 +78,7 @@
 
 project_map = ProjectMap()
 for project in project_map.projects:
-SATestUpdateDiffs.update_reference_results(project)
+SATestUpdateDiffs.update_reference_results(project, args.git)
 
 
 def benchmark(parser, args):
@@ -293,7 +293,8 @@
 "update",
 help="Update static analyzer reference results based on the previous "
 "run of SATest build. Assumes that SATest build was just run.")
-# TODO: add option to decide whether we should use git
+upd_parser.add_argument("--git", action="store_true",
+help="Stage updated results using git.")
 upd_parser.set_defaults(func=update)
 
 # docker subcommand


Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -15,7 +15,7 @@
 Verbose = 0
 
 
-def update_reference_results(project: ProjectInfo):
+def update_reference_results(project: ProjectInfo, git: bool = False):
 test_info = SATestBuild.TestInfo(project)
 tester = SATestBuild.ProjectTester(test_info)
 project_dir = tester.get_project_dir()
@@ -27,9 +27,10 @@
 created_results_path = tester.get_output_dir()
 
 if not os.path.exists(created_results_path):
-print("New results not found, was SATestBuild.py previously run?",
+print(f"Skipping project '{project.name}', "
+  f"it doesn't have newer results.",
   file=sys.stderr)
-sys.exit(1)
+return
 
 build_log_path = SATestBuild.get_build_log_path(ref_results_path)
 build_log_dir = os.path.dirname(

[PATCH] D83660: [analyzer] Fix a crash for dereferencing an empty llvm::Optional variable in SMTConstraintManager.h.

2020-07-22 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

In D83660#2167391 , @NoQ wrote:

> In D83660#2166917 , @steakhal wrote:
>
> > Although I'm not the most experienced one here, I think it's good to go.
>
>
> Let's wait for the test to be added, sounds like it's close.
>
> (@OikawaKirie, you can use delta debugging tools like `creduce` or `delta` to 
> automatically write test cases for crashes)


+1 for the test.  It's not like we don't trust the fix, it is pretty obvious.  
However, it would be good to cover it for the future because it might help with 
tests around this area of code.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83660/new/

https://reviews.llvm.org/D83660



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D84453: [clang-tidy] Suppress one unittest under ASan.

2020-07-23 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko accepted this revision.
vsavchenko added a comment.
This revision is now accepted and ready to land.

LGTM, thanks for taking care of this!


Repository:
  rCTE Clang Tools Extra

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D84453/new/

https://reviews.llvm.org/D84453



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D84494: [Analyzer] Use of BugType in DereferenceChecker (NFC).

2020-07-28 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp:215
   // The explicit NULL case.
+  // We know that 'location' cannot be non-null.  This is what
+  // we call an "explicit" null dereference.

Maybe "is definitely null" here? Otherwise it can be pretty confusing with a 
double negation.



Comment at: 
clang/test/Analysis/Inputs/expected-plists/conditional-path-notes.c.plist:270
descriptionDereference of null pointer (loaded from 
variable 'x')
-   categoryLogic error
+   categoryMemory error
typeDereference of null pointer

I might've missed some discussions on that matter, so please correct me on that.
In my opinion, null pointer dereference is not a memory error.  Null address is 
not a correct memory and never was, so it is a logic error that a special value 
is being interpreted as the pointer to something.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D84494/new/

https://reviews.llvm.org/D84494

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D84843: [Analyzer] Remove inclusion of uniqueing decl from diagnostic profile.

2020-07-29 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko accepted this revision.
vsavchenko added a comment.
This revision is now accepted and ready to land.

LGTM!  Thanks for the fast fix!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D84843/new/

https://reviews.llvm.org/D84843

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D84494: [Analyzer] Use of BugType in DereferenceChecker (NFC).

2020-07-29 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko accepted this revision.
vsavchenko added a comment.

Awesome, thanks!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D84494/new/

https://reviews.llvm.org/D84494

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D84902: [clang-tidy] Fix ODR violation in unittests.

2020-07-30 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko accepted this revision.
vsavchenko added a comment.
This revision is now accepted and ready to land.

LGTM! Thanks for all the investigative work!


Repository:
  rCTE Clang Tools Extra

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D84902/new/

https://reviews.llvm.org/D84902

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D67421: [analyzer] NFC: Move IssueHash to libAnalysis.

2020-07-30 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Other then the naming issue, I don't see any problems with this change!




Comment at: clang/lib/Analysis/IssueHash.cpp:183
 
-std::string clang::GetIssueString(const SourceManager &SM,
-  FullSourceLoc &IssueLoc,
-  StringRef CheckerName, StringRef BugType,
-  const Decl *D,
-  const LangOptions &LangOpts) {
+std::string clang::getIssueStringV1(const FullSourceLoc &IssueLoc,
+StringRef CheckerName,

I'm not a big fan of things like this in names.  First of all, numbers in names 
look bad.  Second, it is not descriptive at all!  I know, I know, it is hard to 
come up with a name that can capture how this `hash` or `string` algorithm is 
different from the other ones, when there are no other ones yet.  And this 
actually brings up the third concern, why do even need to have this suffix now, 
when we don't have other options?  

Let's maybe postpone it till we have the actual motivation to have another 
method, and give them good distinctive names then.  What do you think?


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D67421/new/

https://reviews.llvm.org/D67421

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77551: [analyzer] Fix NSErrorChecker false positives

2020-04-06 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, martong, Charusso, 
dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, 
baloghadamsoftware, xazax.hun.
Herald added a project: clang.

NSErrorChecker used to suggest changing 'void' return type for
constructors and delete operators. This makes little sense because
return types of these functions could not be altered.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77551

Files:
  clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
  clang/test/Analysis/SpecialFunctionsCFError.cpp


Index: clang/test/Analysis/SpecialFunctionsCFError.cpp
===
--- /dev/null
+++ clang/test/Analysis/SpecialFunctionsCFError.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFError \
+// RUN:   -analyzer-store=region -verify %s
+
+typedef unsigned long size_t;
+struct __CFError {};
+typedef struct __CFError *CFErrorRef;
+void *malloc(size_t);
+
+class Foo {
+public:
+  Foo(CFErrorRef *error) {} // no-warning
+
+  void operator delete(void *pointer, CFErrorRef *error) { // no-warning
+return;
+  }
+
+  void operator delete[](void *pointer, CFErrorRef *error) { // no-warning
+return;
+  }
+
+  // Check that we report warnings for operators when it can be useful
+  void operator()(CFErrorRef *error) {} // expected-warning {{Function 
accepting CFErrorRef* should have a non-void return value to indicate whether 
or not an error occurred}}
+};
+
+// Check that global delete operator is not bothered as well
+void operator delete(void *pointer, CFErrorRef *error) { // no-warning
+  return;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -95,6 +95,15 @@
 };
 }
 
+static bool hasReservedReturnType(const FunctionDecl *D) {
+  if (isa(D))
+return true;
+
+  // operators delete and delete[] are required to have 'void' return type
+  auto OperatorKind = D->getOverloadedOperator();
+  return OperatorKind == OO_Delete || OperatorKind == OO_Array_Delete;
+}
+
 void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
 AnalysisManager &mgr,
 BugReporter &BR) const {
@@ -102,6 +111,8 @@
 return;
   if (!D->getReturnType()->isVoidType())
 return;
+  if (hasReservedReturnType(D))
+return;
 
   if (!II)
 II = &D->getASTContext().Idents.get("CFErrorRef");


Index: clang/test/Analysis/SpecialFunctionsCFError.cpp
===
--- /dev/null
+++ clang/test/Analysis/SpecialFunctionsCFError.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFError \
+// RUN:   -analyzer-store=region -verify %s
+
+typedef unsigned long size_t;
+struct __CFError {};
+typedef struct __CFError *CFErrorRef;
+void *malloc(size_t);
+
+class Foo {
+public:
+  Foo(CFErrorRef *error) {} // no-warning
+
+  void operator delete(void *pointer, CFErrorRef *error) { // no-warning
+return;
+  }
+
+  void operator delete[](void *pointer, CFErrorRef *error) { // no-warning
+return;
+  }
+
+  // Check that we report warnings for operators when it can be useful
+  void operator()(CFErrorRef *error) {} // expected-warning {{Function accepting CFErrorRef* should have a non-void return value to indicate whether or not an error occurred}}
+};
+
+// Check that global delete operator is not bothered as well
+void operator delete(void *pointer, CFErrorRef *error) { // no-warning
+  return;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -95,6 +95,15 @@
 };
 }
 
+static bool hasReservedReturnType(const FunctionDecl *D) {
+  if (isa(D))
+return true;
+
+  // operators delete and delete[] are required to have 'void' return type
+  auto OperatorKind = D->getOverloadedOperator();
+  return OperatorKind == OO_Delete || OperatorKind == OO_Array_Delete;
+}
+
 void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
 AnalysisManager &mgr,
 BugReporter &BR) const {
@@ -102,6 +111,8 @@
 return;
   if (!D->getReturnType()->isVoidType())
 return;
+  if (hasReservedReturnType(D))
+return;
 
   if (!II)
 II = &D->getASTContext().Idents.get("CFErrorRef");
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77551: [analyzer] Fix NSErrorChecker false positives

2020-04-06 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked an inline comment as done.
vsavchenko added a comment.

In D77551#1964441 , @NoQ wrote:

> Looks great, thanks! Do you already have commit access or should i commit 
> this for you?


Thanks :-) No, I don't have commit access




Comment at: clang/test/Analysis/SpecialFunctionsCFError.cpp:2
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFError \
+// RUN:   -analyzer-store=region -verify %s
+

Szelethus wrote:
> NoQ wrote:
> > `-analyzer-store=region` is redundant; it's the default and the only 
> > possible value for this option. We should remove it from other tests as 
> > well.
> The default lit command is a bit silly. What does `-setup-analyzer` or 
> something like that even do?
Should I remove it from here in this commit or we should clean up all tests at 
once?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77551/new/

https://reviews.llvm.org/D77551



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, martong, Charusso, 
dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, JDevlieghere, 
szepet, baloghadamsoftware, xazax.hun.
Herald added a project: clang.

We want to trust user type annotations and stop assuming pointers declared
as _Nonnull still can be null.  This functionality is implemented as part
of NullabilityChecker as it tracks non-null types already.  It could
be easily implemented as a part of NSError checker, but it would look like
a simple "shut up" plug as opposed to a more generic solution.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77722

Files:
  clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
  clang/test/Analysis/CheckNSError.m


Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 
-analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError 
-analyzer-store=region -verify -Wno-objc-root-class %s
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,nullability,osx.cocoa.NSError,osx.coreFoundation.CFError 
-analyzer-store=region -verify -Wno-objc-root-class %s
 
 
 typedef signed char BOOL;
@@ -18,6 +18,7 @@
 @interface A
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
 @end
 
 @implementation A
@@ -29,6 +30,11 @@
   if (error) *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; 
// no-warning
   return 0;
 }
+
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error { // no-warning
+  *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
+  return 0;
+}
 @end
 
 struct __CFError {};
Index: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -81,7 +81,7 @@
 : public Checker,
  check::PostCall, check::PostStmt,
  check::PostObjCMessage, check::DeadSymbols,
- check::Event> {
+ check::Location, check::Event> {
   mutable std::unique_ptr BT;
 
 public:
@@ -101,6 +101,8 @@
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
   void checkEvent(ImplicitNullDerefEvent Event) const;
+  void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
 
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
   const char *Sep) const override;
@@ -503,6 +505,41 @@
   }
 }
 
+// Whenever we see a load from a typed memory region that's been annotated as
+// 'nonnull', we want to trust the user on that and assume that it is is indeed
+// non-null.
+void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
+   const Stmt *S,
+   CheckerContext &Context) const {
+  // We should care only about loads.
+  // The main idea is to add a constraint whenever we're loading a value from
+  // an annotated pointer type.
+  if (!IsLoad)
+return;
+
+  // Annotations that we want to consider make sense only for types.
+  auto Region = dyn_cast_or_null(Location.getAsRegion());
+  if (!Region)
+return;
+
+  auto State = Context.getState();
+
+  auto StoredVal = State->getSVal(Region).getAs();
+  if (!StoredVal)
+return;
+
+  auto NullabilityOfTheLoadedValue =
+  getNullabilityAnnotation(Region->getValueType());
+
+  if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
+// It doesn't matter what we think about this particular pointer, it should
+// be considered non-null as annotated by the developer.
+if (auto NewState = State->assume(*StoredVal, true)) {
+  Context.addTransition(NewState);
+}
+  }
+}
+
 /// Find the outermost subexpression of E that is not an implicit cast.
 /// This looks through the implicit casts to _Nonnull that ARC adds to
 /// return expressions of ObjC types when the return type of the function or


Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,nullability,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s
 
 
 typedef signed char BOOL

[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Hi everyone!
I'm thinking about adding support for `__attribute((nonnull))__` as well, but 
it should be handled a bit differently. That annotation is for parameters and 
not for types, so the corresponding constraints should be generated only when 
entering the function (as opposed to each load). I'm not sure that it should be 
a part of this commit though because it will have a completely standalone 
solution. Another question about `__attribute((nonnull))__` is "Where should we 
put it?".  It is quite reasonable to put it into `NullabilityChecker`, but 
`NonnullParamChecker` seems like a good candidate as well.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 255993.
vsavchenko added a comment.

Fix formatting issues in the test file


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722

Files:
  clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
  clang/test/Analysis/CheckNSError.m


Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,5 +1,8 @@
-// RUN: %clang_analyze_cc1 
-analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError 
-analyzer-store=region -verify -Wno-objc-root-class %s
-
+// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=nullability \
+// RUN:   -analyzer-checker=osx.cocoa.NSError \
+// RUN:   -analyzer-checker=osx.coreFoundation.CFError
 
 typedef signed char BOOL;
 typedef int NSInteger;
@@ -18,6 +21,7 @@
 @interface A
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
 @end
 
 @implementation A
@@ -29,6 +33,11 @@
   if (error) *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; 
// no-warning
   return 0;
 }
+
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error { // no-warning
+  *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
+  return 0;
+}
 @end
 
 struct __CFError {};
Index: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -81,7 +81,7 @@
 : public Checker,
  check::PostCall, check::PostStmt,
  check::PostObjCMessage, check::DeadSymbols,
- check::Event> {
+ check::Location, check::Event> {
   mutable std::unique_ptr BT;
 
 public:
@@ -101,6 +101,8 @@
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
   void checkEvent(ImplicitNullDerefEvent Event) const;
+  void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
 
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
   const char *Sep) const override;
@@ -503,6 +505,41 @@
   }
 }
 
+// Whenever we see a load from a typed memory region that's been annotated as
+// 'nonnull', we want to trust the user on that and assume that it is is indeed
+// non-null.
+void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
+   const Stmt *S,
+   CheckerContext &Context) const {
+  // We should care only about loads.
+  // The main idea is to add a constraint whenever we're loading a value from
+  // an annotated pointer type.
+  if (!IsLoad)
+return;
+
+  // Annotations that we want to consider make sense only for types.
+  auto Region = dyn_cast_or_null(Location.getAsRegion());
+  if (!Region)
+return;
+
+  auto State = Context.getState();
+
+  auto StoredVal = State->getSVal(Region).getAs();
+  if (!StoredVal)
+return;
+
+  auto NullabilityOfTheLoadedValue =
+  getNullabilityAnnotation(Region->getValueType());
+
+  if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
+// It doesn't matter what we think about this particular pointer, it should
+// be considered non-null as annotated by the developer.
+if (auto NewState = State->assume(*StoredVal, true)) {
+  Context.addTransition(NewState);
+}
+  }
+}
+
 /// Find the outermost subexpression of E that is not an implicit cast.
 /// This looks through the implicit casts to _Nonnull that ARC adds to
 /// return expressions of ObjC types when the return type of the function or


Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,5 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s
-
+// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=nullability \
+// RUN:   -analyzer-checker=osx.cocoa.NSError \
+// RUN:   -analyzer-checker=osx.coreFoundation.CFError
 
 typedef signed char BOOL;
 typedef int NSInteger;
@@ -18,6 +21,7 @@
 @interface A
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
 @end
 
 @implementation A
@@ -29,6 +33,11 @@
   if (error)

[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked 2 inline comments as done.
vsavchenko added inline comments.



Comment at: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp:521
+  // Annotations that we want to consider make sense only for types.
+  auto Region = dyn_cast_or_null(Location.getAsRegion());
+  if (!Region)

NoQ wrote:
> [[ 
> https://llvm.org/docs/CodingStandards.html#use-auto-type-deduction-to-make-code-more-readable
>  | Too much `auto` ]]!
But this auto is also fine IMO as you can clearly see the actual type in the 
RHS. Mb `const auto *Region` at least?



Comment at: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp:537
+// be considered non-null as annotated by the developer.
+if (auto NewState = State->assume(*StoredVal, true)) {
+  Context.addTransition(NewState);

NoQ wrote:
> Ok, so we're continuing normally if the value is already known to have been 
> assigned to null. We could sink the analysis instead but presumably it's not 
> our job as another checker must have warned before we get there (let's 
> comment about this maybe).
Sure!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

In D77722#1969391 , @Szelethus wrote:

> In D77722#1969261 , @vsavchenko 
> wrote:
>
> > Hi everyone!
> >  I'm thinking about adding support for `__attribute((nonnull))__` as well, 
> > but it should be handled a bit differently. That annotation is for 
> > parameters and not for types, so the corresponding constraints should be 
> > generated only when entering the function (as opposed to each load). I'm 
> > not sure that it should be a part of this commit though because it will 
> > have a completely standalone solution. Another question about 
> > `__attribute((nonnull))__` is "Where should we put it?".  It is quite 
> > reasonable to put it into `NullabilityChecker`, but `NonnullParamChecker` 
> > seems like a good candidate as well.
>
>
> Wait, we don't already assume `nonnull` attributed parameters as, well, not 
> null? That's crazy.


Yep, I was shocked by it as well. Maybe for some warnings it is checked in the 
end and they are truncated before reporting. However, DereferenceChecker 
definitely dispatches `ImplicitNullDerefEvent` for params marked with 
`__attribute__((nonnull))`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256009.
vsavchenko added a comment.

Fix review remarks


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722

Files:
  clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
  clang/test/Analysis/CheckNSError.m

Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,5 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s
-
+// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=nullability \
+// RUN:   -analyzer-checker=osx.cocoa.NSError \
+// RUN:   -analyzer-checker=osx.coreFoundation.CFError
 
 typedef signed char BOOL;
 typedef int NSInteger;
@@ -18,6 +21,7 @@
 @interface A
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
 @end
 
 @implementation A
@@ -29,6 +33,11 @@
   if (error) *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
   return 0;
 }
+
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error { // no-warning
+  *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
+  return 0;
+}
 @end
 
 struct __CFError {};
Index: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -81,7 +81,7 @@
 : public Checker,
  check::PostCall, check::PostStmt,
  check::PostObjCMessage, check::DeadSymbols,
- check::Event> {
+ check::Location, check::Event> {
   mutable std::unique_ptr BT;
 
 public:
@@ -101,6 +101,8 @@
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
   void checkEvent(ImplicitNullDerefEvent Event) const;
+  void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
 
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
   const char *Sep) const override;
@@ -503,6 +505,52 @@
   }
 }
 
+// Whenever we see a load from a typed memory region that's been annotated as
+// 'nonnull', we want to trust the user on that and assume that it is is indeed
+// non-null.
+//
+// We do so even if the value is known to have been assigned to null.
+// The user should be warned on assigning the null value to a non-null pointer
+// as opposed to warning on the later dereference of this pointer.
+//
+// \code
+//   int * _Nonnull var = 0; // we want to warn the user here...
+//   // . . .
+//   *var = 42;  // ...and not here
+// \endcode
+void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
+   const Stmt *S,
+   CheckerContext &Context) const {
+  // We should care only about loads.
+  // The main idea is to add a constraint whenever we're loading a value from
+  // an annotated pointer type.
+  if (!IsLoad)
+return;
+
+  // Annotations that we want to consider make sense only for types.
+  const auto *Region =
+  dyn_cast_or_null(Location.getAsRegion());
+  if (!Region)
+return;
+
+  auto State = Context.getState();
+
+  auto StoredVal = State->getSVal(Region).getAs();
+  if (!StoredVal)
+return;
+
+  auto NullabilityOfTheLoadedValue =
+  getNullabilityAnnotation(Region->getValueType());
+
+  if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
+// It doesn't matter what we think about this particular pointer, it should
+// be considered non-null as annotated by the developer.
+if (auto NewState = State->assume(*StoredVal, true)) {
+  Context.addTransition(NewState);
+}
+  }
+}
+
 /// Find the outermost subexpression of E that is not an implicit cast.
 /// This looks through the implicit casts to _Nonnull that ARC adds to
 /// return expressions of ObjC types when the return type of the function or
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256017.
vsavchenko added a comment.

Get rid of nonessential 'auto' in variable declaration


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722

Files:
  clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
  clang/test/Analysis/CheckNSError.m

Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,5 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s
-
+// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=nullability \
+// RUN:   -analyzer-checker=osx.cocoa.NSError \
+// RUN:   -analyzer-checker=osx.coreFoundation.CFError
 
 typedef signed char BOOL;
 typedef int NSInteger;
@@ -18,6 +21,7 @@
 @interface A
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
 @end
 
 @implementation A
@@ -29,6 +33,11 @@
   if (error) *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
   return 0;
 }
+
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error { // no-warning
+  *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
+  return 0;
+}
 @end
 
 struct __CFError {};
Index: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -81,7 +81,7 @@
 : public Checker,
  check::PostCall, check::PostStmt,
  check::PostObjCMessage, check::DeadSymbols,
- check::Event> {
+ check::Location, check::Event> {
   mutable std::unique_ptr BT;
 
 public:
@@ -101,6 +101,8 @@
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
   void checkEvent(ImplicitNullDerefEvent Event) const;
+  void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
 
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
   const char *Sep) const override;
@@ -503,6 +505,52 @@
   }
 }
 
+// Whenever we see a load from a typed memory region that's been annotated as
+// 'nonnull', we want to trust the user on that and assume that it is is indeed
+// non-null.
+//
+// We do so even if the value is known to have been assigned to null.
+// The user should be warned on assigning the null value to a non-null pointer
+// as opposed to warning on the later dereference of this pointer.
+//
+// \code
+//   int * _Nonnull var = 0; // we want to warn the user here...
+//   // . . .
+//   *var = 42;  // ...and not here
+// \endcode
+void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
+   const Stmt *S,
+   CheckerContext &Context) const {
+  // We should care only about loads.
+  // The main idea is to add a constraint whenever we're loading a value from
+  // an annotated pointer type.
+  if (!IsLoad)
+return;
+
+  // Annotations that we want to consider make sense only for types.
+  const auto *Region =
+  dyn_cast_or_null(Location.getAsRegion());
+  if (!Region)
+return;
+
+  ProgramStateRef State = Context.getState();
+
+  auto StoredVal = State->getSVal(Region).getAs();
+  if (!StoredVal)
+return;
+
+  auto NullabilityOfTheLoadedValue =
+  getNullabilityAnnotation(Region->getValueType());
+
+  if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
+// It doesn't matter what we think about this particular pointer, it should
+// be considered non-null as annotated by the developer.
+if (auto NewState = State->assume(*StoredVal, true)) {
+  Context.addTransition(NewState);
+}
+  }
+}
+
 /// Find the outermost subexpression of E that is not an implicit cast.
 /// This looks through the implicit casts to _Nonnull that ARC adds to
 /// return expressions of ObjC types when the return type of the function or
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256020.
vsavchenko added a comment.

Remove more auto's


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722

Files:
  clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
  clang/test/Analysis/CheckNSError.m

Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -1,5 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s
-
+// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=nullability \
+// RUN:   -analyzer-checker=osx.cocoa.NSError \
+// RUN:   -analyzer-checker=osx.coreFoundation.CFError
 
 typedef signed char BOOL;
 typedef int NSInteger;
@@ -18,6 +21,7 @@
 @interface A
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
 @end
 
 @implementation A
@@ -29,6 +33,11 @@
   if (error) *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
   return 0;
 }
+
+- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error { // no-warning
+  *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
+  return 0;
+}
 @end
 
 struct __CFError {};
Index: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -81,7 +81,7 @@
 : public Checker,
  check::PostCall, check::PostStmt,
  check::PostObjCMessage, check::DeadSymbols,
- check::Event> {
+ check::Location, check::Event> {
   mutable std::unique_ptr BT;
 
 public:
@@ -101,6 +101,8 @@
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
   void checkEvent(ImplicitNullDerefEvent Event) const;
+  void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
 
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
   const char *Sep) const override;
@@ -503,6 +505,52 @@
   }
 }
 
+// Whenever we see a load from a typed memory region that's been annotated as
+// 'nonnull', we want to trust the user on that and assume that it is is indeed
+// non-null.
+//
+// We do so even if the value is known to have been assigned to null.
+// The user should be warned on assigning the null value to a non-null pointer
+// as opposed to warning on the later dereference of this pointer.
+//
+// \code
+//   int * _Nonnull var = 0; // we want to warn the user here...
+//   // . . .
+//   *var = 42;  // ...and not here
+// \endcode
+void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
+   const Stmt *S,
+   CheckerContext &Context) const {
+  // We should care only about loads.
+  // The main idea is to add a constraint whenever we're loading a value from
+  // an annotated pointer type.
+  if (!IsLoad)
+return;
+
+  // Annotations that we want to consider make sense only for types.
+  const auto *Region =
+  dyn_cast_or_null(Location.getAsRegion());
+  if (!Region)
+return;
+
+  ProgramStateRef State = Context.getState();
+
+  auto StoredVal = State->getSVal(Region).getAs();
+  if (!StoredVal)
+return;
+
+  Nullability NullabilityOfTheLoadedValue =
+  getNullabilityAnnotation(Region->getValueType());
+
+  if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
+// It doesn't matter what we think about this particular pointer, it should
+// be considered non-null as annotated by the developer.
+if (ProgramStateRef NewState = State->assume(*StoredVal, true)) {
+  Context.addTransition(NewState);
+}
+  }
+}
+
 /// Find the outermost subexpression of E that is not an implicit cast.
 /// This looks through the implicit casts to _Nonnull that ARC adds to
 /// return expressions of ObjC types when the return type of the function or
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77722: [analyzer] Do not report NSError null dereference for _Nonnull params

2020-04-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

In D77722#1969551 , @NoQ wrote:

> Thanks!
>
> I'll commit.


Awesome! Thanks!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77722/new/

https://reviews.llvm.org/D77722



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77806: [analyzer] Do not report CFError null dereference for nonnull params

2020-04-09 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, martong, Charusso, 
dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, JDevlieghere, 
szepet, baloghadamsoftware, xazax.hun.
Herald added a project: clang.

We want to trust user type annotations and stop assuming pointers declared
as nonnull still can be null.  This functionality is implemented as part
of NonNullParamChecker because it already checks parameter attributes.
Whenever we start analyzing a new function, we assume that all parameters
with 'nonnull' attribute are indeed non-null.

Depends on D77722 .


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77806

Files:
  clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
  clang/test/Analysis/CheckNSError.m
  clang/test/Analysis/nonnull.cpp

Index: clang/test/Analysis/nonnull.cpp
===
--- /dev/null
+++ clang/test/Analysis/nonnull.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -verify %s
+
+void nonnull [[gnu::nonnull]] (int *q);
+
+void f1(int *p) {
+  if (p)
+return;
+  nonnull(p); //expected-warning{{nonnull}}
+}
+
+void f2(int *p) {
+  if (p)
+return;
+  auto lambda = [](int *q) __attribute__((nonnull)){};
+  lambda(p); //expected-warning{{nonnull}}
+}
+
+template 
+void variadicNonnull(ARGS... args) __attribute__((nonnull));
+
+void f3(int a, float b, int *p) {
+  if (p)
+return;
+  variadicNonnull(a, b, p); //expected-warning{{nonnull}}
+}
Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -62,4 +62,17 @@
   return 0;
 }
 
+int __attribute__((nonnull)) f4(CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
 
+int __attribute__((nonnull(1))) f5(int *x, CFErrorRef *error) {
+  *error = 0; // expected-warning {{Potential null dereference}}
+  return 0;
+}
+
+int __attribute__((nonnull(2))) f6(int *x, CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -14,8 +14,9 @@
 //
 //===--===//
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/AST/Attr.h"
+#include "clang/Analysis/AnyCall.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -28,44 +29,78 @@
 
 namespace {
 class NonNullParamChecker
-  : public Checker< check::PreCall, EventDispatcher > {
+: public Checker> {
   mutable std::unique_ptr BTAttrNonNull;
   mutable std::unique_ptr BTNullRefArg;
 
 public:
-
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
 
   std::unique_ptr
-  genReportNullAttrNonNull(const ExplodedNode *ErrorN,
-   const Expr *ArgE,
+  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,
unsigned IdxOfArg) const;
   std::unique_ptr
   genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
   const Expr *ArgE) const;
 };
-} // end anonymous namespace
 
-/// \return Bitvector marking non-null attributes.
-static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+template 
+void setBitsAccordingToFunctionAttributes(const CallType &Call,
+  llvm::SmallBitVector &AttrNonNull) {
   const Decl *FD = Call.getDecl();
-  unsigned NumArgs = Call.getNumArgs();
-  llvm::SmallBitVector AttrNonNull(NumArgs);
+
   for (const auto *NonNull : FD->specific_attrs()) {
 if (!NonNull->args_size()) {
-  AttrNonNull.set(0, NumArgs);
+  // Lack of attribute parameters means that all of the parameters are
+  // implicitly marked as non-null.
+  AttrNonNull.set();
   break;
 }
+
 for (const ParamIdx &Idx : NonNull->args()) {
+  // 'nonnull' attribute's parameters are 1-based and should be adjusted to
+  // match actual AST parameter/argument indices.
   unsigned IdxAST = Idx.getASTIndex();
-  if (IdxAST >= NumArgs)
+  if (IdxAST >= AttrNonNull.size())
 continue;
   AttrNonNull.set(IdxAST);
 }
   }
+}
+
+template 
+void setBitsAccordingToParameterAttributes(const CallType &Call,
+   llvm::SmallBitVector &AttrNonNull) {
+  for (const ParmVarDecl *Parameter :

[PATCH] D77806: [analyzer] Do not report CFError null dereference for nonnull params

2020-04-09 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256306.
vsavchenko added a comment.

Add one more test


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77806/new/

https://reviews.llvm.org/D77806

Files:
  clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
  clang/test/Analysis/CheckNSError.m
  clang/test/Analysis/nonnull.cpp

Index: clang/test/Analysis/nonnull.cpp
===
--- /dev/null
+++ clang/test/Analysis/nonnull.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -verify %s
+
+void nonnull [[gnu::nonnull]] (int *q);
+
+void f1(int *p) {
+  if (p)
+return;
+  nonnull(p); //expected-warning{{nonnull}}
+}
+
+void f2(int *p) {
+  if (p)
+return;
+  auto lambda = [](int *q) __attribute__((nonnull)){};
+  lambda(p); //expected-warning{{nonnull}}
+}
+
+template 
+void variadicNonnull(ARGS... args) __attribute__((nonnull));
+
+void f3(int a, float b, int *p) {
+  if (p)
+return;
+  variadicNonnull(a, b, p); //expected-warning{{nonnull}}
+}
+
+int globalVar = 15;
+void moreParamsThanArgs [[gnu::nonnull(2, 4)]] (int a, int *p, int b = 42, int *q = &globalVar);
+
+void f4(int a, int *p) {
+  if (p)
+return;
+  moreParamsThanArgs(a, p); //expected-warning{{nonnull}}
+}
Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -62,4 +62,17 @@
   return 0;
 }
 
+int __attribute__((nonnull)) f4(CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
 
+int __attribute__((nonnull(1))) f5(int *x, CFErrorRef *error) {
+  *error = 0; // expected-warning {{Potential null dereference}}
+  return 0;
+}
+
+int __attribute__((nonnull(2))) f6(int *x, CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -14,8 +14,9 @@
 //
 //===--===//
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/AST/Attr.h"
+#include "clang/Analysis/AnyCall.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -28,44 +29,78 @@
 
 namespace {
 class NonNullParamChecker
-  : public Checker< check::PreCall, EventDispatcher > {
+: public Checker> {
   mutable std::unique_ptr BTAttrNonNull;
   mutable std::unique_ptr BTNullRefArg;
 
 public:
-
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
 
   std::unique_ptr
-  genReportNullAttrNonNull(const ExplodedNode *ErrorN,
-   const Expr *ArgE,
+  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,
unsigned IdxOfArg) const;
   std::unique_ptr
   genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
   const Expr *ArgE) const;
 };
-} // end anonymous namespace
 
-/// \return Bitvector marking non-null attributes.
-static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+template 
+void setBitsAccordingToFunctionAttributes(const CallType &Call,
+  llvm::SmallBitVector &AttrNonNull) {
   const Decl *FD = Call.getDecl();
-  unsigned NumArgs = Call.getNumArgs();
-  llvm::SmallBitVector AttrNonNull(NumArgs);
+
   for (const auto *NonNull : FD->specific_attrs()) {
 if (!NonNull->args_size()) {
-  AttrNonNull.set(0, NumArgs);
+  // Lack of attribute parameters means that all of the parameters are
+  // implicitly marked as non-null.
+  AttrNonNull.set();
   break;
 }
+
 for (const ParamIdx &Idx : NonNull->args()) {
+  // 'nonnull' attribute's parameters are 1-based and should be adjusted to
+  // match actual AST parameter/argument indices.
   unsigned IdxAST = Idx.getASTIndex();
-  if (IdxAST >= NumArgs)
+  if (IdxAST >= AttrNonNull.size())
 continue;
   AttrNonNull.set(IdxAST);
 }
   }
+}
+
+template 
+void setBitsAccordingToParameterAttributes(const CallType &Call,
+   llvm::SmallBitVector &AttrNonNull) {
+  for (const ParmVarDecl *Parameter : Call.parameters()) {
+if (Parameter->hasAttr())
+  AttrNonNull.set(Parameter->getFunctionScopeIndex());
+  }
+}
+
+template 
+llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call,
+ unsigned ExpectedSize) {
+  llvm::SmallBitVector AttrN

[PATCH] D77806: [analyzer] Do not report CFError null dereference for nonnull params

2020-04-09 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256318.
vsavchenko added a comment.

Add forgotten hunk


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77806/new/

https://reviews.llvm.org/D77806

Files:
  clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
  clang/test/Analysis/CheckNSError.m
  clang/test/Analysis/nonnull.cpp

Index: clang/test/Analysis/nonnull.cpp
===
--- /dev/null
+++ clang/test/Analysis/nonnull.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -verify %s
+
+void nonnull [[gnu::nonnull]] (int *q);
+
+void f1(int *p) {
+  if (p)
+return;
+  nonnull(p); //expected-warning{{nonnull}}
+}
+
+void f2(int *p) {
+  if (p)
+return;
+  auto lambda = [](int *q) __attribute__((nonnull)){};
+  lambda(p); //expected-warning{{nonnull}}
+}
+
+template 
+void variadicNonnull(ARGS... args) __attribute__((nonnull));
+
+void f3(int a, float b, int *p) {
+  if (p)
+return;
+  variadicNonnull(a, b, p); //expected-warning{{nonnull}}
+}
+
+int globalVar = 15;
+void moreParamsThanArgs [[gnu::nonnull(2, 4)]] (int a, int *p, int b = 42, int *q = &globalVar);
+
+void f4(int a, int *p) {
+  if (p)
+return;
+  moreParamsThanArgs(a, p); //expected-warning{{nonnull}}
+}
Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -62,4 +62,17 @@
   return 0;
 }
 
+int __attribute__((nonnull)) f4(CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
 
+int __attribute__((nonnull(1))) f5(int *x, CFErrorRef *error) {
+  *error = 0; // expected-warning {{Potential null dereference}}
+  return 0;
+}
+
+int __attribute__((nonnull(2))) f6(int *x, CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -14,8 +14,9 @@
 //
 //===--===//
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/AST/Attr.h"
+#include "clang/Analysis/AnyCall.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -28,44 +29,78 @@
 
 namespace {
 class NonNullParamChecker
-  : public Checker< check::PreCall, EventDispatcher > {
+: public Checker> {
   mutable std::unique_ptr BTAttrNonNull;
   mutable std::unique_ptr BTNullRefArg;
 
 public:
-
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
 
   std::unique_ptr
-  genReportNullAttrNonNull(const ExplodedNode *ErrorN,
-   const Expr *ArgE,
+  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,
unsigned IdxOfArg) const;
   std::unique_ptr
   genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
   const Expr *ArgE) const;
 };
-} // end anonymous namespace
 
-/// \return Bitvector marking non-null attributes.
-static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+template 
+void setBitsAccordingToFunctionAttributes(const CallType &Call,
+  llvm::SmallBitVector &AttrNonNull) {
   const Decl *FD = Call.getDecl();
-  unsigned NumArgs = Call.getNumArgs();
-  llvm::SmallBitVector AttrNonNull(NumArgs);
+
   for (const auto *NonNull : FD->specific_attrs()) {
 if (!NonNull->args_size()) {
-  AttrNonNull.set(0, NumArgs);
+  // Lack of attribute parameters means that all of the parameters are
+  // implicitly marked as non-null.
+  AttrNonNull.set();
   break;
 }
+
 for (const ParamIdx &Idx : NonNull->args()) {
+  // 'nonnull' attribute's parameters are 1-based and should be adjusted to
+  // match actual AST parameter/argument indices.
   unsigned IdxAST = Idx.getASTIndex();
-  if (IdxAST >= NumArgs)
+  if (IdxAST >= AttrNonNull.size())
 continue;
   AttrNonNull.set(IdxAST);
 }
   }
+}
+
+template 
+void setBitsAccordingToParameterAttributes(const CallType &Call,
+   llvm::SmallBitVector &AttrNonNull) {
+  for (const ParmVarDecl *Parameter : Call.parameters()) {
+if (Parameter->hasAttr())
+  AttrNonNull.set(Parameter->getFunctionScopeIndex());
+  }
+}
+
+template 
+llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call,
+ unsigned ExpectedSize) {
+  llvm::SmallBitVector Attr

[PATCH] D77806: [analyzer] Do not report CFError null dereference for nonnull params

2020-04-09 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256331.
vsavchenko added a comment.

Add extra check to be safe


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77806/new/

https://reviews.llvm.org/D77806

Files:
  clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
  clang/test/Analysis/CheckNSError.m
  clang/test/Analysis/nonnull.cpp

Index: clang/test/Analysis/nonnull.cpp
===
--- /dev/null
+++ clang/test/Analysis/nonnull.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -verify %s
+
+void nonnull [[gnu::nonnull]] (int *q);
+
+void f1(int *p) {
+  if (p)
+return;
+  nonnull(p); //expected-warning{{nonnull}}
+}
+
+void f2(int *p) {
+  if (p)
+return;
+  auto lambda = [](int *q) __attribute__((nonnull)){};
+  lambda(p); //expected-warning{{nonnull}}
+}
+
+template 
+void variadicNonnull(ARGS... args) __attribute__((nonnull));
+
+void f3(int a, float b, int *p) {
+  if (p)
+return;
+  variadicNonnull(a, b, p); //expected-warning{{nonnull}}
+}
+
+int globalVar = 15;
+void moreParamsThanArgs [[gnu::nonnull(2, 4)]] (int a, int *p, int b = 42, int *q = &globalVar);
+
+void f4(int a, int *p) {
+  if (p)
+return;
+  moreParamsThanArgs(a, p); //expected-warning{{nonnull}}
+}
Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -62,4 +62,17 @@
   return 0;
 }
 
+int __attribute__((nonnull)) f4(CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
 
+int __attribute__((nonnull(1))) f5(int *x, CFErrorRef *error) {
+  *error = 0; // expected-warning {{Potential null dereference}}
+  return 0;
+}
+
+int __attribute__((nonnull(2))) f6(int *x, CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -14,8 +14,9 @@
 //
 //===--===//
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/AST/Attr.h"
+#include "clang/Analysis/AnyCall.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -28,44 +29,82 @@
 
 namespace {
 class NonNullParamChecker
-  : public Checker< check::PreCall, EventDispatcher > {
+: public Checker> {
   mutable std::unique_ptr BTAttrNonNull;
   mutable std::unique_ptr BTNullRefArg;
 
 public:
-
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
 
   std::unique_ptr
-  genReportNullAttrNonNull(const ExplodedNode *ErrorN,
-   const Expr *ArgE,
+  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,
unsigned IdxOfArg) const;
   std::unique_ptr
   genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
   const Expr *ArgE) const;
 };
-} // end anonymous namespace
 
-/// \return Bitvector marking non-null attributes.
-static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+template 
+void setBitsAccordingToFunctionAttributes(const CallType &Call,
+  llvm::SmallBitVector &AttrNonNull) {
   const Decl *FD = Call.getDecl();
-  unsigned NumArgs = Call.getNumArgs();
-  llvm::SmallBitVector AttrNonNull(NumArgs);
+
   for (const auto *NonNull : FD->specific_attrs()) {
 if (!NonNull->args_size()) {
-  AttrNonNull.set(0, NumArgs);
+  // Lack of attribute parameters means that all of the parameters are
+  // implicitly marked as non-null.
+  AttrNonNull.set();
   break;
 }
+
 for (const ParamIdx &Idx : NonNull->args()) {
+  // 'nonnull' attribute's parameters are 1-based and should be adjusted to
+  // match actual AST parameter/argument indices.
   unsigned IdxAST = Idx.getASTIndex();
-  if (IdxAST >= NumArgs)
+  if (IdxAST >= AttrNonNull.size())
 continue;
   AttrNonNull.set(IdxAST);
 }
   }
+}
+
+template 
+void setBitsAccordingToParameterAttributes(const CallType &Call,
+   llvm::SmallBitVector &AttrNonNull) {
+  for (const ParmVarDecl *Parameter : Call.parameters()) {
+unsigned ParameterIndex = Parameter->getFunctionScopeIndex();
+if (ParameterIndex == AttrNonNull.size())
+  break;
+
+if (Parameter->hasAttr())
+  AttrNonNull.set(ParameterIndex);
+  }
+}
+
+template 
+llvm::SmallBitVector getNonNullAttrsImpl

[PATCH] D77806: [analyzer] Do not report CFError null dereference for nonnull params

2020-04-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked 4 inline comments as done.
vsavchenko added a comment.

In D77806#1973616 , @NoQ wrote:

> We're doing much more here than just fixing the CFError behavior; we're 
> actually making the whole analyzer respect these annotations in top frame.
>
> Let's add checker-inspecific tests. We have a test checker 
> `debug.ExprInspection` just for that:
>
>   // RUN: %clang_analyze_cc1 ... -analyzer-checker=debug.ExprInspection ...
>   void clang_analyzer_eval(bool);
>  
>   void foo(void *x) __attribute__((nonnull)) {
> clang_analyzer_eval(x != nullptr); // expected-warning{{TRUE}}
>   }
>


Cool, I didn't know about that! I'll add a group of tests for user annotations 
then.




Comment at: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp:218
 
+/// We want to trust developer annotations and consider all 'nonnul' parameters
+/// as non-null indeed. Each marked parameter will get a corresponding

NoQ wrote:
> Typo :p
Whoops! It even got me a full minute or two to spot it now!



Comment at: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp:234
+/// \endcode
+void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const {
+  const LocationContext *LocContext = Context.getLocationContext();

NoQ wrote:
> As far as i understand, you should only do all this for //top-level// 
> functions, i.e. the ones from which we've started the analysis. You can skip 
> inlined functions here because a similar assumption is already made for their 
> parameters in `checkPreCall`.
> 
> You can figure out if you're in top frame via `Context.inTopFrame()`.
Gotcha!



Comment at: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp:238-239
+  const Decl *FD = LocContext->getDecl();
+  // AnyCall helps us here to avoid checking for FunctionDecl and 
ObjCMethodDecl
+  // separately and aggregates interfaces of these classes.
+  auto AbstractCall = AnyCall::forDecl(FD);

NoQ wrote:
> Nice! Should we add new tests for ObjC method calls as well then?
Good catch!



Comment at: clang/test/Analysis/nonnull.cpp:28
+int globalVar = 15;
+void moreParamsThanArgs [[gnu::nonnull(2, 4)]] (int a, int *p, int b = 42, int 
*q = &globalVar);
+

NoQ wrote:
> C-style variadic functions are the real problem. Variadic templates are easy; 
> they're just duplicated in the AST as many times as necessary and all the 
> parameter declarations are in place. Default arguments are also easy; the 
> argument expression is still present in the AST even if it's not explicitly 
> written down at the call site. C-style variadic functions are hard because 
> they actually have more arguments than they have parameters.
C-style variadic functions are already covered in `nonnull.m`. I added here 
C++-specific cases.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77806/new/

https://reviews.llvm.org/D77806



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D77806: [analyzer] Do not report CFError null dereference for nonnull params

2020-04-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 256530.
vsavchenko added a comment.

Fix review remarks


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77806/new/

https://reviews.llvm.org/D77806

Files:
  clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
  clang/test/Analysis/CheckNSError.m
  clang/test/Analysis/UserNullabilityAnnotations.m
  clang/test/Analysis/nonnull.cpp

Index: clang/test/Analysis/nonnull.cpp
===
--- /dev/null
+++ clang/test/Analysis/nonnull.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -verify %s
+
+void nonnull [[gnu::nonnull]] (int *q);
+
+void f1(int *p) {
+  if (p)
+return;
+  nonnull(p); //expected-warning{{nonnull}}
+}
+
+void f2(int *p) {
+  if (p)
+return;
+  auto lambda = [](int *q) __attribute__((nonnull)){};
+  lambda(p); //expected-warning{{nonnull}}
+}
+
+template 
+void variadicNonnull(ARGS... args) __attribute__((nonnull));
+
+void f3(int a, float b, int *p) {
+  if (p)
+return;
+  variadicNonnull(a, b, p); //expected-warning{{nonnull}}
+}
+
+int globalVar = 15;
+void moreParamsThanArgs [[gnu::nonnull(2, 4)]] (int a, int *p, int b = 42, int *q = &globalVar);
+
+void f4(int a, int *p) {
+  if (p)
+return;
+  moreParamsThanArgs(a, p); //expected-warning{{nonnull}}
+}
Index: clang/test/Analysis/UserNullabilityAnnotations.m
===
--- /dev/null
+++ clang/test/Analysis/UserNullabilityAnnotations.m
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=nullability \
+// RUN:   -analyzer-checker=debug.ExprInspection
+
+void clang_analyzer_eval(int);
+
+@interface TestFunctionLevelAnnotations
+- (void)method1:(int *_Nonnull)x;
+- (void)method2:(int *)x __attribute__((nonnull));
+@end
+
+@implementation TestFunctionLevelAnnotations
+- (void)method1:(int *_Nonnull)x {
+  clang_analyzer_eval(x != 0); // expected-warning{{TRUE}}
+}
+
+- (void)method2:(int *)x {
+  clang_analyzer_eval(x != 0); // expected-warning{{TRUE}}
+}
+@end
+
+typedef struct NestedNonnullMember {
+  struct NestedNonnullMember *Child;
+  int *_Nonnull Value;
+} NestedNonnullMember;
+
+NestedNonnullMember *foo();
+
+void f1(NestedNonnullMember *Root) {
+  NestedNonnullMember *Grandson = Root->Child->Child;
+
+  clang_analyzer_eval(Root->Value != 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(Grandson->Value != 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(foo()->Child->Value != 0); // expected-warning{{TRUE}}
+}
Index: clang/test/Analysis/CheckNSError.m
===
--- clang/test/Analysis/CheckNSError.m
+++ clang/test/Analysis/CheckNSError.m
@@ -22,6 +22,7 @@
 - (void)myMethodWhichMayFail:(NSError **)error;
 - (BOOL)myMethodWhichMayFail2:(NSError **)error;
 - (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error;
+- (BOOL)myMethodWhichMayFail4:(NSError **)error __attribute__((nonnull));
 @end
 
 @implementation A
@@ -38,6 +39,11 @@
   *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
   return 0;
 }
+
+- (BOOL)myMethodWhichMayFail4:(NSError **)error { // no-warning
+  *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning
+  return 0;
+}
 @end
 
 struct __CFError {};
@@ -62,4 +68,17 @@
   return 0;
 }
 
+int __attribute__((nonnull)) f4(CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
 
+int __attribute__((nonnull(1))) f5(int *x, CFErrorRef *error) {
+  *error = 0; // expected-warning {{Potential null dereference}}
+  return 0;
+}
+
+int __attribute__((nonnull(2))) f6(int *x, CFErrorRef *error) {
+  *error = 0; // no-warning
+  return 0;
+}
Index: clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
===
--- clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -14,8 +14,9 @@
 //
 //===--===//
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/AST/Attr.h"
+#include "clang/Analysis/AnyCall.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -28,44 +29,82 @@
 
 namespace {
 class NonNullParamChecker
-  : public Checker< check::PreCall, EventDispatcher > {
+: public Checker> {
   mutable std::unique_ptr BTAttrNonNull;
   mutable std::unique_ptr BTNullRefArg;
 
 public:
-
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
 
   std::unique_ptr

[PATCH] D78286: [analyzer] Track runtime types represented by Obj-C Class objects

2020-04-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, martong, Charusso, 
dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, 
baloghadamsoftware, xazax.hun.
Herald added a project: clang.

Objective-C Class objects can be used to do a dynamic dispatch on
class methods. The analyzer had a few places where we tried to overcome
the dynamic nature of it and still guess the actual function that
is being called. That was done mostly using some simple heuristics
covering the most widespread cases (e.g. [[self class] classmethod]).
This solution introduces a way to track types represented by Class
objects and work with that instead of direct AST matching.

rdar://problem/50739539


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D78286

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
  clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
  clang/lib/StaticAnalyzer/Core/CallEvent.cpp
  clang/lib/StaticAnalyzer/Core/DynamicType.cpp
  clang/test/Analysis/cast-value-state-dump.cpp
  clang/test/Analysis/class-object-state-dump.m
  clang/test/Analysis/inlining/InlineObjCClassMethod.m
  clang/test/Analysis/inlining/ObjCDynTypePopagation.m
  clang/test/Analysis/inlining/ObjCDynTypePropagation.m
  clang/test/Analysis/retain-release-inline.m

Index: clang/test/Analysis/retain-release-inline.m
===
--- clang/test/Analysis/retain-release-inline.m
+++ clang/test/Analysis/retain-release-inline.m
@@ -13,6 +13,7 @@
 // It includes the basic definitions for the test cases below.
 //===--===//
 #define NULL 0
+#define nil ((id)0)
 typedef unsigned int __darwin_natural_t;
 typedef unsigned long uintptr_t;
 typedef unsigned int uint32_t;
@@ -21,14 +22,14 @@
 typedef signed long CFIndex;
 typedef CFIndex CFByteOrder;
 typedef struct {
-CFIndex location;
-CFIndex length;
+  CFIndex location;
+  CFIndex length;
 } CFRange;
 static __inline__ __attribute__((always_inline)) CFRange CFRangeMake(CFIndex loc, CFIndex len) {
-CFRange range;
-range.location = loc;
-range.length = len;
-return range;
+  CFRange range;
+  range.location = loc;
+  range.length = len;
+  return range;
 }
 typedef const void * CFTypeRef;
 typedef const struct __CFString * CFStringRef;
@@ -91,6 +92,7 @@
 - (BOOL)isEqual:(id)object;
 - (id)retain;
 - (oneway void)release;
+- (Class)class;
 - (id)autorelease;
 - (id)init;
 @end  @protocol NSCopying  - (id)copyWithZone:(NSZone *)zone;
@@ -100,6 +102,7 @@
 @interface NSObject  {}
 + (id)allocWithZone:(NSZone *)zone;
 + (id)alloc;
++ (Class)class;
 - (void)dealloc;
 @end
 @interface NSObject (NSCoderMethods)
@@ -481,3 +484,33 @@
   [self test_inline_tiny_when_reanalyzing];
 }
 @end
+
+// Original problem: rdar://problem/50739539
+@interface MyClassThatLeaksDuringInit : NSObject
+
++ (MyClassThatLeaksDuringInit *)getAnInstance1;
++ (MyClassThatLeaksDuringInit *)getAnInstance2;
+
+@end
+
+@implementation MyClassThatLeaksDuringInit
+
++ (MyClassThatLeaksDuringInit *)getAnInstance1 {
+  return [[[MyClassThatLeaksDuringInit alloc] init] autorelease]; // expected-warning{{leak}}
+}
+
++ (MyClassThatLeaksDuringInit *)getAnInstance2 {
+  return self class] alloc] init] autorelease]; // expected-warning{{leak}}
+}
+
+- (instancetype)init {
+  if (1) {
+return nil;
+  }
+
+  if (nil != (self = [super init])) {
+  }
+  return self;
+}
+
+@end
Index: clang/test/Analysis/inlining/ObjCDynTypePropagation.m
===
--- clang/test/Analysis/inlining/ObjCDynTypePropagation.m
+++ clang/test/Analysis/inlining/ObjCDynTypePropagation.m
@@ -7,68 +7,67 @@
 PublicSubClass2 *getObj();
 
 @implementation PublicParent
-- (int) getZeroOverridden {
-   return 1;
+- (int)getZeroOverridden {
+  return 1;
 }
-- (int) getZero {
-   return 0;
+- (int)getZero {
+  return 0;
 }
 @end
 
 @implementation PublicSubClass2
-- (int) getZeroOverridden {
-   return 0;
+- (int)getZeroOverridden {
+  return 0;
 }
 
 /* Test that we get the right type from call to alloc. */
-+ (void) testAllocSelf {
++ (void)testAllocSelf {
   id a = [self alloc];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-
-+ (void) testAllocClass {
++ (void)testAllocClass {
   id a = [PublicSubClass2 alloc];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testAllocSuperOverriden {
++ (void)testAllocSuperOverriden {
   id a = [super alloc];
   // Evaluates to 1 in the parent.
-  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}} 
+  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}}
 }
 
-+ (void) testAllocSuper {
++ (void)t

[PATCH] D78286: [analyzer] Track runtime types represented by Obj-C Class objects

2020-04-16 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko updated this revision to Diff 258003.
vsavchenko added a comment.

Fix unfinished comment


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78286/new/

https://reviews.llvm.org/D78286

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
  clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
  clang/lib/StaticAnalyzer/Core/CallEvent.cpp
  clang/lib/StaticAnalyzer/Core/DynamicType.cpp
  clang/test/Analysis/cast-value-state-dump.cpp
  clang/test/Analysis/class-object-state-dump.m
  clang/test/Analysis/inlining/InlineObjCClassMethod.m
  clang/test/Analysis/inlining/ObjCDynTypePopagation.m
  clang/test/Analysis/inlining/ObjCDynTypePropagation.m
  clang/test/Analysis/retain-release-inline.m

Index: clang/test/Analysis/retain-release-inline.m
===
--- clang/test/Analysis/retain-release-inline.m
+++ clang/test/Analysis/retain-release-inline.m
@@ -13,6 +13,7 @@
 // It includes the basic definitions for the test cases below.
 //===--===//
 #define NULL 0
+#define nil ((id)0)
 typedef unsigned int __darwin_natural_t;
 typedef unsigned long uintptr_t;
 typedef unsigned int uint32_t;
@@ -21,14 +22,14 @@
 typedef signed long CFIndex;
 typedef CFIndex CFByteOrder;
 typedef struct {
-CFIndex location;
-CFIndex length;
+  CFIndex location;
+  CFIndex length;
 } CFRange;
 static __inline__ __attribute__((always_inline)) CFRange CFRangeMake(CFIndex loc, CFIndex len) {
-CFRange range;
-range.location = loc;
-range.length = len;
-return range;
+  CFRange range;
+  range.location = loc;
+  range.length = len;
+  return range;
 }
 typedef const void * CFTypeRef;
 typedef const struct __CFString * CFStringRef;
@@ -91,6 +92,7 @@
 - (BOOL)isEqual:(id)object;
 - (id)retain;
 - (oneway void)release;
+- (Class)class;
 - (id)autorelease;
 - (id)init;
 @end  @protocol NSCopying  - (id)copyWithZone:(NSZone *)zone;
@@ -100,6 +102,7 @@
 @interface NSObject  {}
 + (id)allocWithZone:(NSZone *)zone;
 + (id)alloc;
++ (Class)class;
 - (void)dealloc;
 @end
 @interface NSObject (NSCoderMethods)
@@ -481,3 +484,33 @@
   [self test_inline_tiny_when_reanalyzing];
 }
 @end
+
+// Original problem: rdar://problem/50739539
+@interface MyClassThatLeaksDuringInit : NSObject
+
++ (MyClassThatLeaksDuringInit *)getAnInstance1;
++ (MyClassThatLeaksDuringInit *)getAnInstance2;
+
+@end
+
+@implementation MyClassThatLeaksDuringInit
+
++ (MyClassThatLeaksDuringInit *)getAnInstance1 {
+  return [[[MyClassThatLeaksDuringInit alloc] init] autorelease]; // expected-warning{{leak}}
+}
+
++ (MyClassThatLeaksDuringInit *)getAnInstance2 {
+  return self class] alloc] init] autorelease]; // expected-warning{{leak}}
+}
+
+- (instancetype)init {
+  if (1) {
+return nil;
+  }
+
+  if (nil != (self = [super init])) {
+  }
+  return self;
+}
+
+@end
Index: clang/test/Analysis/inlining/ObjCDynTypePropagation.m
===
--- clang/test/Analysis/inlining/ObjCDynTypePropagation.m
+++ clang/test/Analysis/inlining/ObjCDynTypePropagation.m
@@ -7,68 +7,67 @@
 PublicSubClass2 *getObj();
 
 @implementation PublicParent
-- (int) getZeroOverridden {
-   return 1;
+- (int)getZeroOverridden {
+  return 1;
 }
-- (int) getZero {
-   return 0;
+- (int)getZero {
+  return 0;
 }
 @end
 
 @implementation PublicSubClass2
-- (int) getZeroOverridden {
-   return 0;
+- (int)getZeroOverridden {
+  return 0;
 }
 
 /* Test that we get the right type from call to alloc. */
-+ (void) testAllocSelf {
++ (void)testAllocSelf {
   id a = [self alloc];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-
-+ (void) testAllocClass {
++ (void)testAllocClass {
   id a = [PublicSubClass2 alloc];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testAllocSuperOverriden {
++ (void)testAllocSuperOverriden {
   id a = [super alloc];
   // Evaluates to 1 in the parent.
-  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}} 
+  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}}
 }
 
-+ (void) testAllocSuper {
++ (void)testAllocSuper {
   id a = [super alloc];
   clang_analyzer_eval([a getZero] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testAllocInit {
++ (void)testAllocInit {
   id a = [[self alloc] init];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testNewSelf {
++ (void)testNewSelf {
   id a = [self new];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-// Casting to parent should not pessimize the dynamic type. 
-+ (void) testCastToParent {
- id a = [[self alloc] init];
- PublicParent *p = a;  
+// Casting to parent sho

[PATCH] D81315: [analyzer][Draft] [Prototype] warning for default constructed unique pointer dereferences

2020-06-08 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Great start!
I think you are on the right track, so maybe this code won't be thrown away at 
all :-)
Try to work on tests and get familiar with `lit`.




Comment at: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp:31
 namespace {
-class SmartPtrModeling : public Checker {
+struct RegionState {
+private:

xazax.hun wrote:
> I think `RegionState` is not very descriptive. I'd call it something like 
> `RegionNullness`.
linter: LLVM coding standards require to use `class` keyword in situations like 
this 
(https://llvm.org/docs/CodingStandards.html#use-of-class-and-struct-keywords).  
I would even say that `struct` is good for POD types.



Comment at: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp:33
+private:
+  enum Kind { Null, NonNull, Unknown } K;
+  RegionState(Kind InK) : K(InK) {}

I think that it would be better to put declarations for `enum` and for a field 
separately.
Additionally, I don't think that `K` is a very good name for a data member.  It 
should be evident from the name of the member what is it.  Shot names like that 
can be fine only for iterators or for certain `clang`-specific structures 
because of existing traditions (like `SM` for `SourceManager` and `LO` for 
`LanguageOptions`).



Comment at: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp:119
+  ProgramStateRef State = C.getState();
+  const auto OC = dyn_cast(&Call);
+  if (!OC)

xazax.hun wrote:
> Here the const applies for the pointer, not the pointee. We usually do `const 
> auto *OC` instead.
As I said above, I think we should be really careful about abbreviated and 
extremely short variable names.  Longer names make it much easier to read the 
code without paying a lot of attention to declarations.



Comment at: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp:160-187
+  if (const auto IC = dyn_cast(&Call)) {
+const auto MethodDecl = dyn_cast_or_null(IC->getDecl());
+if (!MethodDecl)
+  return;
+auto ArgsNum = IC->getNumArgs();
+
+if (ArgsNum == 0 && isResetMethod(MethodDecl)) {

Considering the fact that more cases and more functions will get supported in 
the future, I vote for merging common parts of these two blocks.



Comment at: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp:230
+
+bool SmartPtrModeling::isResetMethod(const CXXMethodDecl *MethodDec) const {
+  if (!MethodDec)

I believe that methods (and data members related to them) can be `static`.



Comment at: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp:233
+return false;
+  if (MethodDec->getDeclName().isIdentifier()) {
+return ResetMethods.count(MethodDec->getName().lower());

I'm not sure about it myself, but can `DeclName` be `isEmpty()`?  If yes, it is 
a potential null-pointer dereference.  Again, I don't know it for a fact, but I 
think it should be checked.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D81315/new/

https://reviews.llvm.org/D81315



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80669: [analyzer] LoopWidening: fix crash by avoiding aliased references invalidation

2020-06-09 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko added a comment.

Done, sorry for taking so long!
Thank you for your work!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80669/new/

https://reviews.llvm.org/D80669



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D80669: [analyzer] LoopWidening: fix crash by avoiding aliased references invalidation

2020-06-09 Thread Valeriy Savchenko via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG29353e69d25c: [analyzer] LoopWidening: fix crash by avoiding 
aliased references invalidation (authored by AbbasSabra, committed by 
vsavchenko).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80669/new/

https://reviews.llvm.org/D80669

Files:
  clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
  clang/test/Analysis/loop-widening-preserve-reference-type.cpp


Index: clang/test/Analysis/loop-widening-preserve-reference-type.cpp
===
--- clang/test/Analysis/loop-widening-preserve-reference-type.cpp
+++ clang/test/Analysis/loop-widening-preserve-reference-type.cpp
@@ -12,3 +12,11 @@
   for (int i = 0; i < 10; ++i) { }
   clang_analyzer_eval(&x != 0); // expected-warning{{TRUE}}
 }   // expected-warning@-1{{reference cannot be 
bound to dereferenced null pointer in well-defined C++ code; comparison may be 
assumed to always evaluate to true}}
+
+using AR = const A &;
+void invalid_type_alias_region_access() {
+  AR x = B();
+  for (int i = 0; i < 10; ++i) {
+  }
+  clang_analyzer_eval(&x != 0); // expected-warning{{TRUE}}
+} // expected-warning@-1{{reference cannot be bound to dereferenced null 
pointer in well-defined C++ code; comparison may be assumed to always evaluate 
to true}}
Index: clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
===
--- clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -67,8 +67,10 @@
   }
 
   // References should not be invalidated.
-  auto Matches = 
match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef,
-   *LCtx->getDecl()->getBody(), ASTCtx);
+  auto Matches = match(
+  findAll(stmt(hasDescendant(
+  
varDecl(hasType(hasCanonicalType(referenceType(.bind(MatchRef,
+  *LCtx->getDecl()->getBody(), ASTCtx);
   for (BoundNodes Match : Matches) {
 const VarDecl *VD = Match.getNodeAs(MatchRef);
 assert(VD);


Index: clang/test/Analysis/loop-widening-preserve-reference-type.cpp
===
--- clang/test/Analysis/loop-widening-preserve-reference-type.cpp
+++ clang/test/Analysis/loop-widening-preserve-reference-type.cpp
@@ -12,3 +12,11 @@
   for (int i = 0; i < 10; ++i) { }
   clang_analyzer_eval(&x != 0); // expected-warning{{TRUE}}
 }   // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}}
+
+using AR = const A &;
+void invalid_type_alias_region_access() {
+  AR x = B();
+  for (int i = 0; i < 10; ++i) {
+  }
+  clang_analyzer_eval(&x != 0); // expected-warning{{TRUE}}
+} // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}}
Index: clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
===
--- clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -67,8 +67,10 @@
   }
 
   // References should not be invalidated.
-  auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef,
-   *LCtx->getDecl()->getBody(), ASTCtx);
+  auto Matches = match(
+  findAll(stmt(hasDescendant(
+  varDecl(hasType(hasCanonicalType(referenceType(.bind(MatchRef,
+  *LCtx->getDecl()->getBody(), ASTCtx);
   for (BoundNodes Match : Matches) {
 const VarDecl *VD = Match.getNodeAs(MatchRef);
 assert(VD);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D81565: [analyzer] SATestAdd.py: Parse arguments with argparse

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81565

Files:
  clang/utils/analyzer/SATestAdd.py

Index: clang/utils/analyzer/SATestAdd.py
===
--- clang/utils/analyzer/SATestAdd.py
+++ clang/utils/analyzer/SATestAdd.py
@@ -45,18 +45,18 @@
 import SATestBuild
 from ProjectMap import ProjectMap, ProjectInfo
 
+import argparse
 import os
 import sys
 
 
-def add_new_project(name: str, build_mode: int):
+def add_new_project(project: ProjectInfo):
 """
 Add a new project for testing: build it and add to the Project Map file.
 :param name: is a short string used to identify a project.
 """
 
-project_info = ProjectInfo(name, build_mode)
-test_info = SATestBuild.TestInfo(project_info,
+test_info = SATestBuild.TestInfo(project,
  is_reference_build=True)
 tester = SATestBuild.ProjectTester(test_info)
 
@@ -71,37 +71,55 @@
 # Add the project name to the project map.
 project_map = ProjectMap(should_exist=False)
 
-if is_existing_project(project_map, name):
-print(f"Warning: Project with name '{name}' already exists.",
+if is_existing_project(project_map, project):
+print(f"Warning: Project with name '{project.name}' already exists.",
   file=sys.stdout)
 print("Reference output has been regenerated.", file=sys.stdout)
 else:
-project_map.projects.append(project_info)
+project_map.projects.append(project)
 project_map.save()
 
 
-def is_existing_project(project_map: ProjectMap, project_name: str) -> bool:
-return any(existing_project.name == project_name
+def is_existing_project(project_map: ProjectMap, project: ProjectInfo) -> bool:
+return any(existing_project.name == project.name
for existing_project in project_map.projects)
 
 
-# TODO: Use argparse
 # TODO: Add an option not to build.
 # TODO: Set the path to the Repository directory.
 if __name__ == "__main__":
-if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
-print("Add a new project for testing to the analyzer"
-  "\nUsage: ", sys.argv[0],
-  "project_ID \n"
-  "mode: 0 for single file project, "
-  "1 for scan_build, "
-  "2 for single file c++11 project", file=sys.stderr)
-sys.exit(-1)
-
-build_mode = 1
-if len(sys.argv) >= 3:
-build_mode = int(sys.argv[2])
-
-assert((build_mode == 0) | (build_mode == 1) | (build_mode == 2))
-
-add_new_project(sys.argv[1], build_mode)
+parser = argparse.ArgumentParser()
+
+parser.add_argument("name", nargs=1, help="Name of the new project")
+parser.add_argument("--mode", action="store", default=1, type=int,
+choices=[0, 1, 2],
+help="Build mode: 0 for single file project, "
+"1 for scan_build, "
+"2 for single file c++11 project")
+parser.add_argument("--source", action="store", default="script",
+choices=["script", "git", "zip"],
+help=f"Source type of the new project: "
+f"'git' for getting from git "
+f"(please provide --origin and --commit), "
+f"'zip' for unpacking source from a zip file, "
+f"'script' for downloading source by running "
+f"a custom script {SATestBuild.DOWNLOAD_SCRIPT}")
+parser.add_argument("--origin", action="store", default="",
+help="Origin link for a git repository")
+parser.add_argument("--commit", action="store", default="",
+help="Git hash for a commit to checkout")
+
+args = parser.parse_args()
+
+if args.source == "git" and (args.origin == "" or args.commit == ""):
+parser.error(
+"Please provide both --origin and --commit if source is 'git'")
+
+if args.source != "git" and (args.origin != "" or args.commit != ""):
+parser.error("Options --origin and --commit don't make sense when "
+ "source is not 'git'")
+
+project = ProjectInfo(args.name[0], args.mode, args.source, args.origin,
+  args.commit)
+
+add_new_project(project)
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D81564: [analyzer] SATest: Add posibility to download source from git and zip

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81564

Files:
  clang/utils/analyzer/ProjectMap.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -44,7 +44,7 @@
 """
 import CmpRuns
 import SATestUtils
-from ProjectMap import ProjectInfo, ProjectMap
+from ProjectMap import DownloadType, ProjectInfo, ProjectMap
 
 import argparse
 import glob
@@ -57,6 +57,7 @@
 import sys
 import threading
 import time
+import zipfile
 
 from queue import Queue
 # mypy has problems finding InvalidFileException in the module
@@ -198,63 +199,6 @@
verbose=VERBOSE)
 
 
-def download_and_patch(directory: str, build_log_file: IO):
-"""
-Download the project and apply the local patchfile if it exists.
-"""
-cached_source = os.path.join(directory, CACHED_SOURCE_DIR_NAME)
-
-# If the we don't already have the cached source, run the project's
-# download script to download it.
-if not os.path.exists(cached_source):
-download(directory, build_log_file)
-if not os.path.exists(cached_source):
-stderr(f"Error: '{cached_source}' not found after download.\n")
-exit(1)
-
-patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
-
-# Remove potentially stale patched source.
-if os.path.exists(patched_source):
-shutil.rmtree(patched_source)
-
-# Copy the cached source and apply any patches to the copy.
-shutil.copytree(cached_source, patched_source, symlinks=True)
-apply_patch(directory, build_log_file)
-
-
-def download(directory: str, build_log_file: IO):
-"""
-Run the script to download the project, if it exists.
-"""
-script_path = os.path.join(directory, DOWNLOAD_SCRIPT)
-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):
-patchfile_path = os.path.join(directory, PATCHFILE_NAME)
-patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
-
-if not os.path.exists(patchfile_path):
-stdout("  No local patches.\n")
-return
-
-stdout("  Applying patch.\n")
-try:
-check_call(f"patch -p1 < '{patchfile_path}'",
-   cwd=patched_source,
-   stderr=build_log_file,
-   stdout=build_log_file,
-   shell=True)
-
-except CalledProcessError:
-stderr(f"Error: Patch failed. "
-   f"See {build_log_file.name} for details.\n")
-sys.exit(1)
-
-
 class TestInfo(NamedTuple):
 """
 Information about a project and settings for its analysis.
@@ -430,7 +374,7 @@
 # Build and analyze the project.
 with open(build_log_path, "w+") as build_log_file:
 if self.project.mode == 1:
-download_and_patch(directory, build_log_file)
+self._download_and_patch(directory, build_log_file)
 run_cleanup_script(directory, build_log_file)
 self.scan_build(directory, output_dir, build_log_file)
 else:
@@ -587,6 +531,100 @@
 
 return out
 
+def _download_and_patch(self, directory: str, build_log_file: IO):
+"""
+Download the project and apply the local patchfile if it exists.
+"""
+cached_source = os.path.join(directory, CACHED_SOURCE_DIR_NAME)
+
+# If the we don't already have the cached source, run the project's
+# download script to download it.
+if not os.path.exists(cached_source):
+self._download(directory, build_log_file)
+if not os.path.exists(cached_source):
+stderr(f"Error: '{cached_source}' not found after download.\n")
+exit(1)
+
+patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
+
+# Remove potentially stale patched source.
+if os.path.exists(patched_source):
+shutil.rmtree(patched_source)
+
+# Copy the cached source and apply any patches to the copy.
+shutil.copytree(cached_source, patched_source, symlinks=True)
+self._apply_patch(directory, build_log_file)
+
+def _download(self, directory: str, build_log_file: IO):
+"""
+Run the script to download the project, if it exists.
+"""
+if self.project.source == DownloadType.GIT:
+self._download_from_git(directory, build_log_file)
+

[PATCH] D81563: [analyzer] SATest: Move from csv to json project maps

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

JSON format is a bit more verbose and easier to reason about
and extend.  For this reason, before extending SATestBuild
functionality it is better to refactor the part of how we
configure the whole system.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81563

Files:
  clang/utils/analyzer/ProjectMap.py
  clang/utils/analyzer/SATestAdd.py
  clang/utils/analyzer/SATestBuild.py
  clang/utils/analyzer/SATestUpdateDiffs.py

Index: clang/utils/analyzer/SATestUpdateDiffs.py
===
--- clang/utils/analyzer/SATestUpdateDiffs.py
+++ clang/utils/analyzer/SATestUpdateDiffs.py
@@ -4,6 +4,7 @@
 Update reference results for static analyzer.
 """
 import SATestBuild
+from ProjectMap import ProjectInfo, ProjectMap
 
 import os
 import shutil
@@ -14,9 +15,9 @@
 Verbose = 0
 
 
-def update_reference_results(project_name: str, build_mode: int):
-project_info = SATestBuild.ProjectInfo(project_name, build_mode)
-tester = SATestBuild.ProjectTester(project_info)
+def update_reference_results(project: ProjectInfo):
+test_info = SATestBuild.TestInfo(project)
+tester = SATestBuild.ProjectTester(test_info)
 project_dir = tester.get_project_dir()
 
 tester.is_reference_build = True
@@ -54,7 +55,7 @@
 SATestBuild.run_cleanup_script(project_dir, build_log_file)
 
 SATestBuild.normalize_reference_results(
-project_dir, ref_results_path, build_mode)
+project_dir, ref_results_path, project.mode)
 
 # Clean up the generated difference results.
 SATestBuild.cleanup_reference_results(ref_results_path)
@@ -62,6 +63,7 @@
 run_cmd(f"git add '{ref_results_path}'")
 
 
+# TODO: use argparse
 def main(argv):
 if len(argv) == 2 and argv[1] in ("-h", "--help"):
 print("Update static analyzer reference results based "
@@ -70,9 +72,9 @@
   file=sys.stderr)
 sys.exit(1)
 
-with open(SATestBuild.get_project_map_path(), "r") as f:
-for project_name, build_mode in SATestBuild.get_projects(f):
-update_reference_results(project_name, int(build_mode))
+project_map = ProjectMap()
+for project in project_map.projects:
+update_reference_results(project)
 
 
 if __name__ == '__main__':
Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -44,9 +44,9 @@
 """
 import CmpRuns
 import SATestUtils
+from ProjectMap import ProjectInfo, ProjectMap
 
 import argparse
-import csv
 import glob
 import logging
 import math
@@ -59,9 +59,11 @@
 import time
 
 from queue import Queue
+# mypy has problems finding InvalidFileException in the module
+# and this is we can shush that false positive
+from plistlib import InvalidFileException  # type:ignore
 from subprocess import CalledProcessError, check_call
-from typing import (cast, Dict, Iterable, IO, List, NamedTuple, Optional,
-Tuple, TYPE_CHECKING)
+from typing import Dict, IO, List, NamedTuple, Optional, TYPE_CHECKING
 
 
 ###
@@ -105,9 +107,6 @@
 # Number of jobs.
 MAX_JOBS = int(math.ceil(multiprocessing.cpu_count() * 0.75))
 
-# Project map stores info about all the "registered" projects.
-PROJECT_MAP_FILE = "projectMap.csv"
-
 # Names of the project specific scripts.
 # The script that downloads the project.
 DOWNLOAD_SCRIPT = "download_project.sh"
@@ -187,18 +186,6 @@
 ###
 
 
-def get_project_map_path(should_exist: bool = True) -> str:
-project_map_path = os.path.join(os.path.abspath(os.curdir),
-PROJECT_MAP_FILE)
-
-if should_exist and not os.path.exists(project_map_path):
-stderr(f"Error: Cannot find the project map file {project_map_path}"
-   f"\nRunning script for the wrong directory?\n")
-sys.exit(1)
-
-return project_map_path
-
-
 def run_cleanup_script(directory: str, build_log_file: IO):
 """
 Run pre-processing script if any.
@@ -268,12 +255,11 @@
 sys.exit(1)
 
 
-class ProjectInfo(NamedTuple):
+class TestInfo(NamedTuple):
 """
 Information about a project and settings for its analysis.
 """
-name: str
-build_mode: int
+project: ProjectInfo
 override_compiler: bool = False
 extra_analyzer_config: str = ""
 is_reference_build: bool = False
@@ -287,9 +273,9 @@
 # It is a common workaround for this situation:
 # https://mypy.readthedocs.io/en/stable/common_issues.html#using

[PATCH] D81566: [analyzer] CmpRuns.py: Decouple main functionality from argparse

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

It makes it much harder to use from other modules when one of the
parameters is an argparse Namespace.  This commit makes it easier
to use CmpRuns programmatically.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81566

Files:
  clang/utils/analyzer/CmpRuns.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -775,12 +775,12 @@
 
 patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
 
-# TODO: get rid of option parser invocation here
-args = CmpRuns.generate_option_parser().parse_args(
-["--root-old", "", "--root-new", patched_source, "", ""])
+ref_results = CmpRuns.ResultsDirectory(ref_dir)
+new_results = CmpRuns.ResultsDirectory(new_dir, patched_source)
+
 # Scan the results, delete empty plist files.
 num_diffs, reports_in_ref, reports_in_new = \
-CmpRuns.dump_scan_build_results_diff(ref_dir, new_dir, args,
+CmpRuns.dump_scan_build_results_diff(ref_results, new_results,
  delete_empty=False,
  out=LOCAL.stdout)
 
Index: clang/utils/analyzer/CmpRuns.py
===
--- clang/utils/analyzer/CmpRuns.py
+++ clang/utils/analyzer/CmpRuns.py
@@ -35,8 +35,9 @@
 from math import log
 from collections import defaultdict
 from copy import copy
-from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
-Tuple, Union)
+from enum import Enum
+from typing import (Any, cast, Dict, List, NamedTuple, Optional, Sequence,
+TextIO, TypeVar, Tuple, Union)
 
 
 Number = Union[int, float]
@@ -58,6 +59,17 @@
 CLEAR = '\x1b[0m'
 
 
+class HistogramType(str, Enum):
+RELATIVE = "relative"
+LOG_RELATIVE = "log-relative"
+ABSOLUTE = "absolute"
+
+
+class ResultsDirectory(NamedTuple):
+path: str
+root: str = ""
+
+
 class SingleRunInfo:
 """
 Information about analysis run:
@@ -65,9 +77,10 @@
 root - the name of the root directory, which will be disregarded when
 determining the source file name
 """
-def __init__(self, path: str, root: str = "", verbose_log=None):
-self.path = path
-self.root = root.rstrip("/\\")
+def __init__(self, results: ResultsDirectory,
+ verbose_log: Optional[str] = None):
+self.path = results.path
+self.root = results.root.rstrip("/\\")
 self.verbose_log = verbose_log
 
 
@@ -232,13 +245,13 @@
 self.diagnostics: List[AnalysisDiagnostic] = []
 
 
-def load_results(path: str, args: argparse.Namespace, root: str = "",
- delete_empty: bool = True) -> AnalysisRun:
+def load_results(results: ResultsDirectory, delete_empty: bool = True,
+ verbose_log: Optional[str] = None) -> AnalysisRun:
 """
 Backwards compatibility API.
 """
-return load_results_from_single_run(SingleRunInfo(path, root,
-  args.verbose_log),
+return load_results_from_single_run(SingleRunInfo(results,
+  verbose_log),
 delete_empty)
 
 
@@ -280,7 +293,8 @@
 
 
 def compare_results(results_old: AnalysisRun, results_new: AnalysisRun,
-args: argparse.Namespace) -> ComparisonResult:
+histogram: Optional[HistogramType] = None
+) -> ComparisonResult:
 """
 compare_results - Generate a relation from diagnostics in run A to
 diagnostics in run B.
@@ -311,15 +325,15 @@
 if a.get_issue_identifier() == b.get_issue_identifier():
 if a.get_path_length() != b.get_path_length():
 
-if args.relative_path_histogram:
+if histogram == HistogramType.RELATIVE:
 path_difference_data.append(
 float(a.get_path_length()) / b.get_path_length())
 
-elif args.relative_log_path_histogram:
+elif histogram == HistogramType.LOG_RELATIVE:
 path_difference_data.append(
 log(float(a.get_path_length()) / b.get_path_length()))
 
-elif args.absolute_path_histogram:
+elif histogram == HistogramType.ABSOLUTE:
 path_difference_data.append(
 a.get_path_length() - b.get_path_length())
 
@@ 

[PATCH] D81569: [analyzer] SATest: Add option to specify projects to test

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81569

Files:
  clang/utils/analyzer/ProjectMap.py
  clang/utils/analyzer/SATest.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -44,7 +44,7 @@
 """
 import CmpRuns
 import SATestUtils
-from ProjectMap import DownloadType, ProjectInfo, ProjectMap
+from ProjectMap import DownloadType, ProjectInfo
 
 import glob
 import logging
@@ -225,10 +225,11 @@
 """
 A component aggregating all of the project testing.
 """
-def __init__(self, jobs: int, override_compiler: bool,
- extra_analyzer_config: str, regenerate: bool,
- strictness: bool):
+def __init__(self, jobs: int, projects: List[ProjectInfo],
+ override_compiler: bool, extra_analyzer_config: str,
+ regenerate: bool, strictness: bool):
 self.jobs = jobs
+self.projects = projects
 self.override_compiler = override_compiler
 self.extra_analyzer_config = extra_analyzer_config
 self.regenerate = regenerate
@@ -237,10 +238,8 @@
 def test_all(self) -> bool:
 projects_to_test: List[TestInfo] = []
 
-project_map = ProjectMap()
-
 # Test the projects.
-for project in project_map.projects:
+for project in self.projects:
 projects_to_test.append(
 TestInfo(project,
  self.override_compiler,
Index: clang/utils/analyzer/SATest.py
===
--- clang/utils/analyzer/SATest.py
+++ clang/utils/analyzer/SATest.py
@@ -28,7 +28,29 @@
 
 def build(parser, args):
 SATestBuild.VERBOSE = args.verbose
-tester = SATestBuild.RegressionTester(args.jobs, args.override_compiler,
+
+project_map = ProjectMap()
+projects = project_map.projects
+
+if args.projects:
+projects_arg = args.projects.split(",")
+available_projects = [project.name
+  for project in projects]
+
+# validate that given projects are present in the project map file
+for manual_project in projects_arg:
+if manual_project not in available_projects:
+parser.error("Project '{project}' is not found in "
+ "the project map file. Available projects are "
+ "{all}.".format(project=manual_project,
+ all=available_projects))
+
+projects = [project.with_fields(enabled=project.name in projects_arg)
+for project in projects]
+
+tester = SATestBuild.RegressionTester(args.jobs,
+  projects,
+  args.override_compiler,
   args.extra_analyzer_config,
   args.regenerate,
   args.strictness)
@@ -111,6 +133,8 @@
   dest="extra_analyzer_config", type=str,
   default="",
   help="Arguments passed to to -analyzer-config")
+build_parser.add_argument("--projects", action="store", default="",
+  help="Comma-separated list of preojects to test")
 build_parser.add_argument("-v", "--verbose", action="count", default=0)
 build_parser.set_defaults(func=build)
 
Index: clang/utils/analyzer/ProjectMap.py
===
--- clang/utils/analyzer/ProjectMap.py
+++ clang/utils/analyzer/ProjectMap.py
@@ -28,6 +28,20 @@
 commit: str = ""
 enabled: bool = True
 
+def with_fields(self, **kwargs) -> "ProjectInfo":
+"""
+Create a copy of this project info with customized fields.
+NamedTuple is immutable and this is a way to create modified copies.
+
+  info.enabled = True
+  info.mode = 1
+
+can be done as follows:
+
+  modified = info.with_fields(enbled=True, mode=1)
+"""
+return ProjectInfo(**{**self._asdict(), **kwargs})
+
 
 class ProjectMap:
 """
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D81568: [analyzer] ProjectMap: Do not serialize fields with default values

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81568

Files:
  clang/utils/analyzer/ProjectMap.py


Index: clang/utils/analyzer/ProjectMap.py
===
--- clang/utils/analyzer/ProjectMap.py
+++ clang/utils/analyzer/ProjectMap.py
@@ -118,4 +118,17 @@
 
 @staticmethod
 def _convert_infos_to_dicts(projects: List[ProjectInfo]) -> List[JSON]:
-return [project._asdict() for project in projects]
+return [ProjectMap._convert_info_to_dict(project)
+for project in projects]
+
+@staticmethod
+def _convert_info_to_dict(project: ProjectInfo) -> JSON:
+whole_dict = project._asdict()
+defaults = project._field_defaults
+
+# there is no need in serializing fields with default values
+for field, default_value in defaults.items():
+if whole_dict[field] == default_value:
+del whole_dict[field]
+
+return whole_dict


Index: clang/utils/analyzer/ProjectMap.py
===
--- clang/utils/analyzer/ProjectMap.py
+++ clang/utils/analyzer/ProjectMap.py
@@ -118,4 +118,17 @@
 
 @staticmethod
 def _convert_infos_to_dicts(projects: List[ProjectInfo]) -> List[JSON]:
-return [project._asdict() for project in projects]
+return [ProjectMap._convert_info_to_dict(project)
+for project in projects]
+
+@staticmethod
+def _convert_info_to_dict(project: ProjectInfo) -> JSON:
+whole_dict = project._asdict()
+defaults = project._field_defaults
+
+# there is no need in serializing fields with default values
+for field, default_value in defaults.items():
+if whole_dict[field] == default_value:
+del whole_dict[field]
+
+return whole_dict
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D81567: [analyzer] SATest: Introduce a single entrypoint for regression scripts

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81567

Files:
  clang/utils/analyzer/CmpRuns.py
  clang/utils/analyzer/SATest.py
  clang/utils/analyzer/SATestAdd.py
  clang/utils/analyzer/SATestBuild.py

Index: clang/utils/analyzer/SATestBuild.py
===
--- clang/utils/analyzer/SATestBuild.py
+++ clang/utils/analyzer/SATestBuild.py
@@ -46,7 +46,6 @@
 import SATestUtils
 from ProjectMap import DownloadType, ProjectInfo, ProjectMap
 
-import argparse
 import glob
 import logging
 import math
@@ -635,7 +634,6 @@
the canonical ones.
 :param failure_flag: Used to signify a failure during the run.
 """
-self.args = args
 self.tasks_queue = tasks_queue
 self.results_differ = results_differ
 self.failure_flag = failure_flag
@@ -883,37 +881,6 @@
 
 
 if __name__ == "__main__":
-# Parse command line arguments.
-parser = argparse.ArgumentParser(
-description="Test the Clang Static Analyzer.")
-
-parser.add_argument("--strictness", dest="strictness", type=int, default=0,
-help="0 to fail on runtime errors, 1 to fail when the "
-"number of found bugs are different from the "
-"reference, 2 to fail on any difference from the "
-"reference. Default is 0.")
-parser.add_argument("-r", dest="regenerate", action="store_true",
-default=False, help="Regenerate reference output.")
-parser.add_argument("--override-compiler", action="store_true",
-default=False, help="Call scan-build with "
-"--override-compiler option.")
-parser.add_argument("-j", "--jobs", dest="jobs", type=int,
-default=0,
-help="Number of projects to test concurrently")
-parser.add_argument("--extra-analyzer-config",
-dest="extra_analyzer_config", type=str,
-default="",
-help="Arguments passed to to -analyzer-config")
-parser.add_argument("-v", "--verbose", action="count", default=0)
-
-args = parser.parse_args()
-
-VERBOSE = args.verbose
-tester = RegressionTester(args.jobs, args.override_compiler,
-  args.extra_analyzer_config, args.regenerate,
-  args.strictness)
-tests_passed = tester.test_all()
-
-if not tests_passed:
-stderr("ERROR: Tests failed.")
-sys.exit(42)
+print("SATestBuild.py should not be used on its own.")
+print("Please use 'SATest.py build' instead")
+sys.exit(1)
Index: clang/utils/analyzer/SATestAdd.py
===
--- clang/utils/analyzer/SATestAdd.py
+++ clang/utils/analyzer/SATestAdd.py
@@ -45,7 +45,6 @@
 import SATestBuild
 from ProjectMap import ProjectMap, ProjectInfo
 
-import argparse
 import os
 import sys
 
@@ -85,41 +84,7 @@
for existing_project in project_map.projects)
 
 
-# TODO: Add an option not to build.
-# TODO: Set the path to the Repository directory.
 if __name__ == "__main__":
-parser = argparse.ArgumentParser()
-
-parser.add_argument("name", nargs=1, help="Name of the new project")
-parser.add_argument("--mode", action="store", default=1, type=int,
-choices=[0, 1, 2],
-help="Build mode: 0 for single file project, "
-"1 for scan_build, "
-"2 for single file c++11 project")
-parser.add_argument("--source", action="store", default="script",
-choices=["script", "git", "zip"],
-help=f"Source type of the new project: "
-f"'git' for getting from git "
-f"(please provide --origin and --commit), "
-f"'zip' for unpacking source from a zip file, "
-f"'script' for downloading source by running "
-f"a custom script {SATestBuild.DOWNLOAD_SCRIPT}")
-parser.add_argument("--origin", action="store", default="",
-help="Origin link for a git repository")
-parser.add_argument("--commit", action="store", default="",
-help="Git hash for a commit to checkout")
-
-args = parser.parse_args()
-
-if args.source == "git" and (args.origin == "" or args.commit == ""):
-parser.error(
-"Please provide both --origin and --commit if source is 'git'")
-
-if args.source != "git" and (args.or

[PATCH] D81571: [analyzer] SATest: Add initial docker infrastructure

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Static analysis is very sensitive to environment.
OS and libraries installed can affect the results.  This fact makes
it extremely hard to have a regression testing system that will
produce stable results.

For this very reason, this commit introduces a new dockerized testing
environment, so that every analyzer developer can check their changes
against previous analysis results.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81571

Files:
  clang/utils/analyzer/Dockerfile
  clang/utils/analyzer/entrypoint.py

Index: clang/utils/analyzer/entrypoint.py
===
--- /dev/null
+++ clang/utils/analyzer/entrypoint.py
@@ -0,0 +1,52 @@
+import argparse
+import os
+
+from typing import List, Tuple
+
+from subprocess import check_call
+
+
+def main():
+settings, rest = parse_arguments()
+if settings.build_llvm or settings.build_llvm_only:
+build_llvm()
+if settings.build_llvm_only:
+return
+test(rest)
+
+
+def parse_arguments() -> Tuple[argparse.Namespace, List[str]]:
+parser = argparse.ArgumentParser()
+parser.add_argument('--build-llvm', action='store_true')
+parser.add_argument('--build-llvm-only', action='store_true')
+return parser.parse_known_args()
+
+
+def build_llvm() -> None:
+os.chdir('/build')
+cmake()
+ninja()
+
+
+CMAKE_COMMAND = "cmake -G Ninja -DCMAKE_BUILD_TYPE=Release " \
+"-DCMAKE_INSTALL_PREFIX=/analyzer -DLLVM_TARGETS_TO_BUILD=X86 " \
+"-DLLVM_ENABLE_PROJECTS=clang -DLLVM_BUILD_RUNTIME=OFF " \
+"-DLLVM_ENABLE_TERMINFO=OFF -DCLANG_ENABLE_ARCMT=OFF " \
+"-DCLANG_ENABLE_STATIC_ANALYZER=ON"
+
+
+def cmake():
+check_call(CMAKE_COMMAND + ' /llvm-project/llvm', shell=True)
+
+
+def ninja():
+check_call("ninja install", shell=True)
+
+
+def test(args: List[str]):
+os.chdir("/projects")
+check_call("/scripts/SATest.py " + " ".join(args), shell=True)
+
+
+if __name__ == '__main__':
+main()
Index: clang/utils/analyzer/Dockerfile
===
--- /dev/null
+++ clang/utils/analyzer/Dockerfile
@@ -0,0 +1,52 @@
+FROM ubuntu:bionic
+
+RUN apt-get update && apt-get install -y \
+apt-transport-https \
+ca-certificates \
+gnupg \
+software-properties-common \
+wget
+
+# newer CMake is required by LLVM
+RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null
+RUN apt-add-repository -y 'deb https://apt.kitware.com/ubuntu/ bionic main'
+
+# test system dependencies
+RUN apt-get update && apt-get install -y \
+git \
+gettext \
+python3 \
+python3-pip \
+cmake \
+ninja-build
+
+# box2d dependencies
+RUN apt-get install -y \
+libx11-dev \
+libxrandr-dev \
+libxinerama-dev \
+libxcursor-dev \
+libxi-dev
+
+# symengine dependencies
+RUN apt-get install -y \
+libgmp10 \
+libgmp-dev
+
+RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1
+
+VOLUME /analyzer
+VOLUME /projects
+VOLUME /llvm-project
+VOLUME /build
+VOLUME /scripts
+
+ENV PATH="/analyzer/bin:${PATH}"
+
+ADD entrypoint.py /entrypoint.py
+
+# Uncomment in case of requirements
+# ADD requirements.txt /requirements.txt
+# RUN pip3 install -r /requirements.txt
+
+ENTRYPOINT ["python", "/entrypoint.py"]
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D81572: [analyzer] SATest: Add convenience 'docker' command

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

It provides a simpler interface for testing within docker.
This way the user is not required to no how to use `docker run` and
its options.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81572

Files:
  clang/utils/analyzer/SATest.py


Index: clang/utils/analyzer/SATest.py
===
--- clang/utils/analyzer/SATest.py
+++ clang/utils/analyzer/SATest.py
@@ -9,6 +9,16 @@
 
 import argparse
 import sys
+import os
+
+from subprocess import check_call
+
+SCRIPTS_DIR = os.path.dirname(os.path.realpath(__file__))
+PROJECTS_DIR = os.path.join(SCRIPTS_DIR, "projects")
+DEFAULT_LLVM_DIR = os.path.realpath(os.path.join(SCRIPTS_DIR,
+ os.path.pardir,
+ os.path.pardir,
+ os.path.pardir))
 
 
 def add(parser, args):
@@ -78,6 +88,37 @@
 SATestUpdateDiffs.update_reference_results(project)
 
 
+def docker(parser, args):
+if len(args.rest) > 0:
+if args.rest[0] != "--":
+parser.error("REST arguments should start with '--'")
+args.rest = args.rest[1:]
+
+if args.build_image:
+docker_build_image()
+else:
+docker_run(args)
+
+
+def docker_build_image():
+check_call("docker build --tag satest-image {}".format(SCRIPTS_DIR),
+   shell=True)
+
+
+def docker_run(args):
+check_call("docker run --rm --name satest "
+   "-v {llvm}:/llvm-project "
+   "-v {build}:/build "
+   "-v {clang}:/analyzer "
+   "-v {scripts}:/scripts "
+   "-v {projects}:/projects "
+   "satest-image:latest {args}"
+   .format(llvm=args.llvm_project_dir, build=args.build_dir,
+   clang=args.clang_dir, scripts=SCRIPTS_DIR,
+   projects=PROJECTS_DIR, args=' '.join(args.rest)),
+   shell=True)
+
+
 def main():
 parser = argparse.ArgumentParser()
 subparsers = parser.add_subparsers()
@@ -180,6 +221,27 @@
 # TODO: add option to decide whether we should use git
 upd_parser.set_defaults(func=update)
 
+# docker subcommand
+dock_parser = subparsers.add_parser(
+"docker",
+help="Run regression system in the docker.")
+
+dock_parser.add_argument("--build-image", action="store_true",
+ help="Build docker image for running tests.")
+dock_parser.add_argument("--llvm-project-dir", action="store",
+ default=DEFAULT_LLVM_DIR,
+ help="Path to LLVM source code. Defaults "
+ "to the repo where this script is located. ")
+dock_parser.add_argument("--build-dir", action="store", default="",
+ help="Path to a directory where docker should "
+ "build LLVM code.")
+dock_parser.add_argument("--clang-dir", action="store", default="",
+ help="Path to find/install LLVM installation.")
+dock_parser.add_argument("rest", nargs=argparse.REMAINDER, default=[],
+ help="Additionall args that will be forwarded "
+ "to the docker's entrypoint.")
+dock_parser.set_defaults(func=docker)
+
 args = parser.parse_args()
 args.func(parser, args)
 


Index: clang/utils/analyzer/SATest.py
===
--- clang/utils/analyzer/SATest.py
+++ clang/utils/analyzer/SATest.py
@@ -9,6 +9,16 @@
 
 import argparse
 import sys
+import os
+
+from subprocess import check_call
+
+SCRIPTS_DIR = os.path.dirname(os.path.realpath(__file__))
+PROJECTS_DIR = os.path.join(SCRIPTS_DIR, "projects")
+DEFAULT_LLVM_DIR = os.path.realpath(os.path.join(SCRIPTS_DIR,
+ os.path.pardir,
+ os.path.pardir,
+ os.path.pardir))
 
 
 def add(parser, args):
@@ -78,6 +88,37 @@
 SATestUpdateDiffs.update_reference_results(project)
 
 
+def docker(parser, args):
+if len(args.rest) > 0:
+if args.rest[0] != "--":
+parser.error("REST arguments should start with '--'")
+args.rest = args.rest[1:]
+
+if args.build_image:
+docker_build_image()
+else:
+docker_run(args)
+
+
+def docker_build_image():
+check_call("docker build --tag satest-image {}".format(SCRIPTS_DIR),
+   shell=True)
+
+
+def docker_run(args):
+check_call("docker run --rm --name satest "
+

[PATCH] D81592: [analyzer] SATest: Add a set of initial projects for testing

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko created this revision.
vsavchenko added reviewers: NoQ, dcoughlin.
Herald added subscribers: cfe-commits, ASDenysPetrov, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, 
xazax.hun.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D81592

Files:
  .gitignore
  clang/utils/analyzer/.dockerignore
  clang/utils/analyzer/projects/box2d/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/box2d/run_static_analyzer.cmd
  clang/utils/analyzer/projects/cxxopts/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/cxxopts/run_static_analyzer.cmd
  clang/utils/analyzer/projects/libsoundio/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/libsoundio/run_static_analyzer.cmd
  clang/utils/analyzer/projects/oatpp/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/oatpp/run_static_analyzer.cmd
  clang/utils/analyzer/projects/projects.json
  clang/utils/analyzer/projects/symengine/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/symengine/run_static_analyzer.cmd
  clang/utils/analyzer/projects/termbox/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/termbox/run_static_analyzer.cmd
  clang/utils/analyzer/projects/tinyexpr/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/tinyexpr/run_static_analyzer.cmd
  clang/utils/analyzer/projects/tinyspline/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/tinyspline/run_static_analyzer.cmd
  clang/utils/analyzer/projects/tinyvm/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/tinyvm/run_static_analyzer.cmd
  clang/utils/analyzer/projects/zstd/cleanup_run_static_analyzer.sh
  clang/utils/analyzer/projects/zstd/run_static_analyzer.cmd

Index: clang/utils/analyzer/projects/zstd/run_static_analyzer.cmd
===
--- /dev/null
+++ clang/utils/analyzer/projects/zstd/run_static_analyzer.cmd
@@ -0,0 +1,2 @@
+cmake ./build/cmake -DCMAKE_BUILD_TYPE=Release -Bbuild_analyzer -GNinja
+cmake --build build_analyzer
Index: clang/utils/analyzer/projects/zstd/cleanup_run_static_analyzer.sh
===
--- /dev/null
+++ clang/utils/analyzer/projects/zstd/cleanup_run_static_analyzer.sh
@@ -0,0 +1 @@
+rm -rf ./build_analyzer
Index: clang/utils/analyzer/projects/tinyvm/run_static_analyzer.cmd
===
--- /dev/null
+++ clang/utils/analyzer/projects/tinyvm/run_static_analyzer.cmd
@@ -0,0 +1 @@
+make
Index: clang/utils/analyzer/projects/tinyvm/cleanup_run_static_analyzer.sh
===
--- /dev/null
+++ clang/utils/analyzer/projects/tinyvm/cleanup_run_static_analyzer.sh
@@ -0,0 +1 @@
+make clean
Index: clang/utils/analyzer/projects/tinyspline/run_static_analyzer.cmd
===
--- /dev/null
+++ clang/utils/analyzer/projects/tinyspline/run_static_analyzer.cmd
@@ -0,0 +1,2 @@
+cmake . -DCMAKE_BUILD_TYPE=Release -Bbuild -GNinja
+cmake --build build
Index: clang/utils/analyzer/projects/tinyspline/cleanup_run_static_analyzer.sh
===
--- /dev/null
+++ clang/utils/analyzer/projects/tinyspline/cleanup_run_static_analyzer.sh
@@ -0,0 +1 @@
+rm -rf ./build
Index: clang/utils/analyzer/projects/tinyexpr/run_static_analyzer.cmd
===
--- /dev/null
+++ clang/utils/analyzer/projects/tinyexpr/run_static_analyzer.cmd
@@ -0,0 +1 @@
+make
Index: clang/utils/analyzer/projects/tinyexpr/cleanup_run_static_analyzer.sh
===
--- /dev/null
+++ clang/utils/analyzer/projects/tinyexpr/cleanup_run_static_analyzer.sh
@@ -0,0 +1 @@
+make clean
Index: clang/utils/analyzer/projects/termbox/run_static_analyzer.cmd
===
--- /dev/null
+++ clang/utils/analyzer/projects/termbox/run_static_analyzer.cmd
@@ -0,0 +1,2 @@
+./waf configure
+./waf
Index: clang/utils/analyzer/projects/termbox/cleanup_run_static_analyzer.sh
===
--- /dev/null
+++ clang/utils/analyzer/projects/termbox/cleanup_run_static_analyzer.sh
@@ -0,0 +1,2 @@
+./waf clean
+exit 0
Index: clang/utils/analyzer/projects/symengine/run_static_analyzer.cmd
===
--- /dev/null
+++ clang/utils/analyzer/projects/symengine/run_static_analyzer.cmd
@@ -0,0 +1,2 @@
+cmake . -DCMAKE_BUILD_TYPE=Release -DWITH_COTIRE=OFF -Bbuild -GNinja
+cmake --build build
Index: clang/utils/analyzer/projects/symengine/cleanup_run_static_analyzer.sh
===
--- /dev/null
+++ clang/utils/analyze

[PATCH] D81567: [analyzer] SATest: Introduce a single entrypoint for regression scripts

2020-06-10 Thread Valeriy Savchenko via Phabricator via cfe-commits
vsavchenko marked an inline comment as done.
vsavchenko added inline comments.



Comment at: clang/utils/analyzer/CmpRuns.py:526-529
+if __name__ == "__main__":
+print("CmpRuns.py should not be used on its own.")
+print("Please use 'SATest.py compare' instead")
+sys.exit(1)

NoQ wrote:
> Should we also `chown -x` these scripts?
That is a great idea!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D81567/new/

https://reviews.llvm.org/D81567



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


  1   2   3   4   5   6   7   8   9   >