diff --git a/.gitignore b/.gitignore index ce20ccb809..3805d8e820 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store *.py[cod] @@ -20,10 +21,10 @@ thrift_api/gen-js/ thrift_api/gen-py/ # test_files build files -tests/test_projects/test_files/src/call_and_message.o -tests/test_projects/test_files/src/divide_zero.o -tests/test_projects/test_files/src/new_delete.o -tests/test_projects/test_files/src/null_dereference.o -tests/test_projects/test_files/src/stack_address_escape.o -tests/test_projects/test_files/src/file_to_be_skipped.o +tests/test_projects/test_files/call_and_message.o +tests/test_projects/test_files/divide_zero.o +tests/test_projects/test_files/new_delete.o +tests/test_projects/test_files/null_dereference.o +tests/test_projects/test_files/stack_address_escape.o +tests/test_projects/test_files/file_to_be_skipped.o diff --git a/.travis.yml b/.travis.yml index 289e3a81b5..643ae41299 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,39 @@ -sudo: required -dist: trusty +language: + - python -language: python +services: + - postgresql -python: - - "2.7" +matrix: + include: + - os: linux + sudo: required + dist: trusty + python: "2.7" + - os: osx + osx_image: xcode6.4 + sudo: false + language: generic + +before_install: + - git clone https://github.com/llvm-mirror/clang.git ~/llvm\ + - chmod a+x ~/llvm/tools/scan-build-py/bin/intercept-build; + - export PATH=~/llvm/tools/scan-build-py/bin:$PATH; + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install python; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install thrift; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install doxygen; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install coreutils; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl http://llvm.org/releases/3.7.0/clang+llvm-3.7.0-x86_64-apple-darwin.tar.xz -o clang+llvm-3.7.0-x86_64-apple-darwin.tar.xz; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then tar xf clang+llvm-3.7.0-x86_64-apple-darwin.tar.xz -C ~/; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH=~/clang+llvm-3.7.0-x86_64-apple-darwin/bin/:$PATH; fi install: - pip install -r ./.ci/python_requirements - pip install nose + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PG_DATA=$(brew --prefix)/var/postgres; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pg_ctl -w start -l postgres.log --pgdata ${PG_DATA}; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then createuser -s postgres && psql -c 'create database travis_ci_test;' -U postgres && cat postgres.log; fi addons: apt: @@ -29,7 +54,7 @@ addons: script: - ./change_clang_version.py - - . tests/test_env_setup.sh + - source tests/test_env_setup.sh - ./build_package.py -o $TEST_CODECHECKER_PACKAGE_DIR -v diff --git a/README.md b/README.md index 9aa42310ff..de13220d20 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - [![Build Status](https://travis-ci.org/Ericsson/codechecker.svg?branch=master)](https://travis-ci.org/Ericsson/codechecker) ----- # Introduction @@ -60,6 +59,56 @@ pip install -r .ci/basic_python_requirements cd .. ~~~~~~ +## OS X + +### Basic dependecy install & setup +Tested on OS X El Capitan 10.11.5 & macOS Sierra 10.12 Beta +~~~~~~{.sh} + +# on El Capitan System Integrity Protection (SIP) need to turn off +- Click the  (Apple) menu. +- Select Restart... +- Hold down command-R to boot into the Recovery System. +- Click the Utilities menu and select Terminal. +- Type csrutil disable and press return. +- Close the Terminal app. +- Click the  (Apple) menu and select Restart.... + +# check out and build clang with extra tools +How to: http://clang.llvm.org/get_started.html + +# get dependencies +brew update +brew install doxygen thrift gcc git + +# get source code +git clone https://github.com/tmsblgh/codechecker-osx-migration.git +cd codechecker + +# install required basic python modules +pip install -r .ci/basic_python_requirements + +# create codechecker package +./build_package.py -o ~/codechecker_package +cd .. +~~~~~~ + +### Check a test project +#### Check if clang or clang tidy is available +~~~~~~{.sh} +which clang +which clang-tidy +~~~~~~ +If 'clang' or 'clang-tidy' commands are not available the package can be configured to use another/newer clang binary for the analisys. +Edit the 'CodeChecker/config/package_layout.json' config files "runtime/analyzers" +section in the generated package and modify the analyzers section to the analyzers +available in the PATH +``` +"analyzers" : { + "clangsa" : "clang", + "clang-tidy" : "clang-tidy" + }, +``` ### Check a test project #### Check if clang or clang tidy is available @@ -88,6 +137,14 @@ This step can be skipped if you always give the path of CodeChecker command. ~~~~~~{.sh} export PATH=~/codechecker_package/CodeChecker/bin:$PATH ~~~~~~ +Scan-build-py (intercept-build) +~~~~~~{.sh} +export PATH=~/{user path}/llvm/tools/clang/tools/scan-build-py/bin:$PATH +~~~~~~ +Clang +~~~~~~{.sh} +export PATH=~/{user path}/build/bin:$PATH +~~~~~~ #### Check the project Check the project using SQLite. The database is placed in the working diff --git a/build_package.py b/build_package.py index 1e612ddf16..70450bf896 100755 --- a/build_package.py +++ b/build_package.py @@ -17,6 +17,9 @@ import subprocess import time import shlex +import platform + +from distutils.spawn import find_executable LOG = logging.getLogger('Packager') @@ -190,6 +193,13 @@ def handle_external_file(dep, clean, env, verbose): download_cmd.extend(shlex.split(source_package['download_cmd'])) file_url = source_package['url'] download_cmd.append(file_url) + + option = source_package['option'] + download_cmd.append(option) + + file_name = source_package['name'] + download_cmd.append(file_name) + LOG.info('Downloading ...') if run_cmd(download_cmd, directory, env, verbose): LOG.error('Failed to get dependency') @@ -207,6 +217,7 @@ def handle_external_file(dep, clean, env, verbose): file_name = os.path.join(directory, file_name) with tarfile.open(file_name) as tar: tar.extractall(directory) + os.remove(file_name) else: LOG.error('Unsupported file type') elif file_ext in supported_exts['uncompressed']: @@ -354,42 +365,53 @@ def build_package(repository_root, build_package_config, env=None): # create package folder layout create_folder_layout(output_dir, package_layout) - # build ld logger - ld_logger_path = build_package_config['ld_logger_path'] - if ld_logger_path: - ld_logger_build = os.path.join(ld_logger_path, 'build') - - ld_logger32 = build_package_config.get('ld_logger_32') - ld_logger64 = build_package_config.get('ld_logger_64') - rebuild = build_package_config.get('rebuild_ld_logger') or clean + # check scan-build-py (intercept) + LOG.info('Checking source: llvm scan-build-py (intercept)') - arch = None - if ld_logger32 == ld_logger64: - # build both versions - pass - elif ld_logger32: - arch = '32' - elif ld_logger64: - arch = '64' - - if build_ld_logger(ld_logger_path, env, arch, rebuild, verbose): - LOG.error('Failed to build ld logger') - sys.exit() + intercept_build_executable = find_executable('intercept-build') + + if intercept_build_executable != None: + LOG.info('Available') + else: + LOG.error('Not exists, build ld logger in Linux only') + # build ld logger because intercept is not available + if platform.system() == 'Linux': + ld_logger_path = build_package_config['ld_logger_path'] + if ld_logger_path: + ld_logger_build = os.path.join(ld_logger_path, 'build') + + ld_logger32 = build_package_config.get('ld_logger_32') + ld_logger64 = build_package_config.get('ld_logger_64') + rebuild = build_package_config.get('rebuild_ld_logger') or clean + + arch = None + if ld_logger32 == ld_logger64: + # build both versions + pass + elif ld_logger32: + arch = '32' + elif ld_logger64: + arch = '64' + + if build_ld_logger(ld_logger_path, env, arch, rebuild, verbose): + LOG.error('Failed to build ld logger') + sys.exit() - # copy ld logger files - target = os.path.join(package_root, package_layout['ld_logger']) + # copy ld logger files + target = os.path.join(package_root, package_layout['ld_logger']) - copy_tree(ld_logger_build, target) + copy_tree(ld_logger_build, target) - curr_dir = os.getcwd() - os.chdir(os.path.join(package_root, package_layout['bin'])) - logger_symlink = os.path.join('../', package_layout['ld_logger'], - 'bin', 'ldlogger') - os.symlink(logger_symlink, 'ldlogger') - os.chdir(curr_dir) + curr_dir = os.getcwd() + os.chdir(os.path.join(package_root, package_layout['bin'])) + logger_symlink = os.path.join('../', package_layout['ld_logger'], + 'bin', 'ldlogger') + os.symlink(logger_symlink, 'ldlogger') + os.chdir(curr_dir) - else: - LOG.info('Skipping ld logger from package') + else: + LOG.info('Skipping ld logger from package') + # generate gen files with thrift thrift_files_dir = os.path.join(repository_root, 'thrift_api') diff --git a/change_clang_version.py b/change_clang_version.py index 9831157327..1c9716989d 100755 --- a/change_clang_version.py +++ b/change_clang_version.py @@ -1,13 +1,17 @@ #!/usr/bin/env python import json +import platform data = None with open('config/package_layout.json') as f: data = json.load(f) -data['runtime']['analyzers']['clangsa'] = 'clang-3.7' +if platform.system() == 'Linux': + data['runtime']['analyzers']['clangsa'] = 'clang-3.7' +if platform.system() == 'Darwin': + data['runtime']['analyzers']['clangsa'] = 'clang' with open('config/package_layout.json', 'w') as f: json.dump(data, f, indent=4) diff --git a/codechecker_lib/analysis_manager.py b/codechecker_lib/analysis_manager.py index 44f1383731..417e3000ba 100644 --- a/codechecker_lib/analysis_manager.py +++ b/codechecker_lib/analysis_manager.py @@ -71,6 +71,7 @@ def check(check_data): try: # if one analysis fails the check fails return_codes = 0 + skipped = False for source in action.sources: # if there is no skiplist handler there was no skip list file diff --git a/codechecker_lib/build_manager.py b/codechecker_lib/build_manager.py index 3535e77eb3..2bce8265bb 100644 --- a/codechecker_lib/build_manager.py +++ b/codechecker_lib/build_manager.py @@ -10,9 +10,14 @@ import pickle import subprocess import sys +import shutil +import platform from codechecker_lib import logger from codechecker_lib import analyzer_env +from codechecker_lib import host_check + +from distutils.spawn import find_executable LOG = logger.get_new_logger('BUILD MANAGER') @@ -60,11 +65,26 @@ def perform_build_command(logfile, command, context, silent=False): original_env = os.environ.copy() return_code = 0 - # Run user's commands in shell - log_env = analyzer_env.get_log_env(logfile, context, original_env) - if 'CC_LOGGER_GCC_LIKE' not in log_env: - log_env['CC_LOGGER_GCC_LIKE'] = 'gcc:g++:clang:clang++:cc:c++' + # Run user's commands with intercept + if host_check.check_intercept(original_env): + LOG.debug_analyzer("with intercept ...") + final_command = command + command = "intercept-build " + "--cdb "+ logfile + " " + final_command + log_env = original_env + LOG.debug_analyzer(command) + + # Run user's commands in shell + else: + # TODO better platform detection + if platform.system() == 'Linux': + LOG.debug_analyzer("with ld logger ...") + log_env = analyzer_env.get_log_env(logfile, context, original_env) + if 'CC_LOGGER_GCC_LIKE' not in log_env: + log_env['CC_LOGGER_GCC_LIKE'] = 'gcc:g++:clang:clang++:cc:c++' + else: + LOG.error("Intercept-build is required to run CodeChecker in OS X.") + sys.exit(1) LOG.debug_analyzer(log_env) try: @@ -124,16 +144,21 @@ def generate_log_file(args, context, silent=False): log_file = None try: if args.command: - # check if logger bin exists - if not os.path.isfile(context.path_logger_bin): - LOG.debug_analyzer('Logger binary not found! Required for logging.') - sys.exit(1) - - # check if logger lib exists - if not os.path.exists(context.path_logger_lib): - LOG.debug_analyzer('Logger library directory not found! Libs are requires' \ - 'for logging.') - sys.exit(1) + + intercept_build_executable = find_executable('intercept-build') + + if intercept_build_executable == None: + if platform.system() == 'Linux': + # check if logger bin exists + if not os.path.isfile(context.path_logger_bin): + LOG.error('Logger binary not found! Required for logging.') + sys.exit(1) + + # check if logger lib exists + if not os.path.exists(context.path_logger_lib): + LOG.error('Logger library directory not found! Libs are requires' \ + 'for logging.') + sys.exit(1) log_file = default_compilation_db(args.workspace) if os.path.exists(log_file): diff --git a/codechecker_lib/host_check.py b/codechecker_lib/host_check.py index 71823bfc89..6ba620f0f8 100644 --- a/codechecker_lib/host_check.py +++ b/codechecker_lib/host_check.py @@ -108,3 +108,30 @@ def check_clang(compiler_bin, env): LOG.error(oerr) LOG.error('Failed to run: ' + ' '.join(clang_version_cmd) + '"') return False + +# ----------------------------------------------------------------------------- +def check_intercept(env): + ''' + simple check if intercept (scan-build-py) is available + ''' + intercept_cmd = ['intercept-build'] + try: + res = subprocess.call(intercept_cmd, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + if not res: + return True + else: + LOG.debug('Failed to run: "' + ' '.join(intercept_cmd) + '"') + return False + + except OSError as oerr: + if oerr[0] == errno.ENOENT: + # not just intercept-build can be used for logging + # it is possible that another build logger is available + LOG.debug(oerr) + LOG.debug('Failed to run: ' + ' '.join(intercept_cmd) + '"') + return False + diff --git a/external-source-deps/ext_source_deps_config.json b/external-source-deps/ext_source_deps_config.json index 9d6c7d096e..e4bbc0ef2a 100644 --- a/external-source-deps/ext_source_deps_config.json +++ b/external-source-deps/ext_source_deps_config.json @@ -37,7 +37,7 @@ "version" : "1.10.4", "description" : "dojo", "homepage" : "http://dojotoolkit.org/", - "source_package" : { "download_cmd" : "wget" , "url" : "http://download.dojotoolkit.org/release-1.10.4/dojo-release-1.10.4.tar.gz" } + "source_package" : { "download_cmd" : "curl" , "url" : "http://download.dojotoolkit.org/release-1.10.4/dojo-release-1.10.4.tar.gz" , "option" : "-o" , "name" : "dojo-release-1.10.4.tar.gz" } }, { "name" : "highlightjs", @@ -45,7 +45,7 @@ "version" : "9.0.0", "description" : "highlightjs", "homepage" : "https://highlightjs.org/", - "source_package" : { "download_cmd" : "wget" , "url" : "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js" } + "source_package" : { "download_cmd" : "curl" , "url" : "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js" , "option" : "-o" , "name" : "highlight.min.js" } }, { "name" : "highlightjs_css", @@ -53,6 +53,6 @@ "version" : "9.0.0", "description" : "highlightjs_css", "homepage" : "https://highlightjs.org/", - "source_package" : { "download_cmd" : "wget" , "url" : "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/styles/xcode.min.css" } + "source_package" : { "download_cmd" : "curl" , "url" : "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/styles/xcode.min.css" , "option" : "-o" , "name" : "xcode.min.css" } } ] diff --git a/tests/DeallocUseAfterFreeErrors.m b/tests/DeallocUseAfterFreeErrors.m new file mode 100644 index 0000000000..862b1f3f84 --- /dev/null +++ b/tests/DeallocUseAfterFreeErrors.m @@ -0,0 +1,378 @@ +// This testfile is a copy from clang's github repository +// https://github.com/llvm-mirror/clang/blob/master/test/Analysis/DeallocUseAfterFreeErrors.m + +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.SuperDealloc,debug.ExprInspection -analyzer-output=text -verify %s +// CodeChecker check -n Obj -b "make" --analyzers clangsa -e osx.cocoa.SuperDealloc // makefile: all: clang -c DeallocUseAfterFreeErrors.m -o DeallocUseAfterFreeErrors + + +void clang_analyzer_warnIfReached(); + +#define nil ((id)0) + +typedef unsigned long NSUInteger; +@protocol NSObject +- (instancetype)retain; +- (oneway void)release; +@end + +@interface NSObject { } +- (void)dealloc; +- (instancetype)init; +@end + +typedef struct objc_selector *SEL; + +//===------------------------------------------------------------------------=== +// +// Check that 'self' is not referenced after calling '[super dealloc]'. + +@interface SuperDeallocThenReleaseIvarClass : NSObject { + NSObject *_ivar; +} +@end + +@implementation SuperDeallocThenReleaseIvarClass +- (instancetype)initWithIvar:(NSObject *)ivar { + self = [super init]; + if (!self) + return nil; + _ivar = [ivar retain]; + return self; +} +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [_ivar release]; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} +} +@end + +@interface SuperDeallocThenAssignNilToIvarClass : NSObject { + NSObject *_delegate; +} +@end + +@implementation SuperDeallocThenAssignNilToIvarClass +- (instancetype)initWithDelegate:(NSObject *)delegate { + self = [super init]; + if (!self) + return nil; + _delegate = delegate; + return self; +} +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + _delegate = nil; // expected-warning {{Use of instance variable '_delegate' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_delegate' after 'self' has been deallocated}} +} +@end + + +struct SomeStruct { + int f; +}; + +@interface SuperDeallocThenAssignIvarField : NSObject { + struct SomeStruct _s; +} +@end + +@implementation SuperDeallocThenAssignIvarField +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + _s.f = 7; // expected-warning {{Use of instance variable '_s' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_s' after 'self' has been deallocated}} +} +@end + +@interface OtherClassWithIvar { +@public + int _otherIvar; +} +@end; + +@interface SuperDeallocThenAssignIvarIvar : NSObject { + OtherClassWithIvar *_ivar; +} +@end + +@implementation SuperDeallocThenAssignIvarIvar +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + _ivar->_otherIvar = 7; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} +} +@end + +@interface SuperDeallocThenAssignSelfIvar : NSObject { + NSObject *_ivar; +} +@end + +@implementation SuperDeallocThenAssignSelfIvar +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + self->_ivar = nil; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} +} +@end + +@interface SuperDeallocThenReleasePropertyClass : NSObject { } +@property (retain) NSObject *ivar; +@end + +@implementation SuperDeallocThenReleasePropertyClass +- (instancetype)initWithProperty:(NSObject *)ivar { + self = [super init]; + if (!self) + return nil; + self.ivar = ivar; + return self; +} +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + self.ivar = nil; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} +} +@end + +@interface SuperDeallocThenAssignNilToPropertyClass : NSObject { } +@property (assign) NSObject *delegate; +@end + +@implementation SuperDeallocThenAssignNilToPropertyClass +- (instancetype)initWithDelegate:(NSObject *)delegate { + self = [super init]; + if (!self) + return nil; + self.delegate = delegate; + return self; +} +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + self.delegate = nil; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} +} +@end + +@interface SuperDeallocThenCallInstanceMethodClass : NSObject { } +- (void)_invalidate; +@end + +@implementation SuperDeallocThenCallInstanceMethodClass +- (void)_invalidate { +} +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [self _invalidate]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} +} +@end + +@interface SuperDeallocThenCallNonObjectiveCMethodClass : NSObject { } +@end + +static void _invalidate(NSObject *object) { + (void)object; +} + +@implementation SuperDeallocThenCallNonObjectiveCMethodClass +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + _invalidate(self); // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} +} +@end + +@interface SuperDeallocThenCallObjectiveClassMethodClass : NSObject { } +@end + +@implementation SuperDeallocThenCallObjectiveClassMethodClass ++ (void) invalidate:(id)arg; { +} + +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [SuperDeallocThenCallObjectiveClassMethodClass invalidate:self]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} +} +@end + +@interface TwoSuperDeallocCallsClass : NSObject { + NSObject *_ivar; +} +- (void)_invalidate; +@end + +@implementation TwoSuperDeallocCallsClass +- (void)_invalidate { +} +- (void)dealloc { + if (_ivar) { // expected-note {{Taking false branch}} + [_ivar release]; + [super dealloc]; + return; + } + [super dealloc]; // expected-note {{[super dealloc] called here}} + [self _invalidate]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} +} +@end + +//===------------------------------------------------------------------------=== +// Warn about calling [super dealloc] twice due to missing return statement. + +@interface MissingReturnCausesDoubleSuperDeallocClass : NSObject { + NSObject *_ivar; +} +@end + +@implementation MissingReturnCausesDoubleSuperDeallocClass +- (void)dealloc { + if (_ivar) { // expected-note {{Taking true branch}} + [_ivar release]; + [super dealloc]; // expected-note {{[super dealloc] called here}} + // return; + } + [super dealloc]; // expected-warning{{[super dealloc] should not be called multiple times}} + // expected-note@-1{{[super dealloc] should not be called multiple times}} +} +@end + +//===------------------------------------------------------------------------=== +// Warn about calling [super dealloc] twice in two different methods. + +@interface SuperDeallocInOtherMethodClass : NSObject { + NSObject *_ivar; +} +- (void)_cleanup; +@end + +@implementation SuperDeallocInOtherMethodClass +- (void)_cleanup { + [_ivar release]; + [super dealloc]; // expected-note {{[super dealloc] called here}} +} +- (void)dealloc { + [self _cleanup]; // expected-note {{Calling '_cleanup'}} + //expected-note@-1 {{Returning from '_cleanup'}} + [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} + // expected-note@-1 {{[super dealloc] should not be called multiple times}} +} +@end + +//===------------------------------------------------------------------------=== +// Do not warn about calling [super dealloc] recursively for different objects +// of the same type with custom retain counting. +// +// A class that contains an ivar of itself with custom retain counting (such +// as provided by _OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN) can generate +// a false positive that [super dealloc] is called twice if each object instance +// is not tracked separately by the checker. This test case is just a simple +// approximation to trigger the false positive. + +@class ClassWithOwnIvarInstanceClass; +@interface ClassWithOwnIvarInstanceClass : NSObject { + ClassWithOwnIvarInstanceClass *_ivar; + NSUInteger _retainCount; +} +@end + +@implementation ClassWithOwnIvarInstanceClass +- (instancetype)retain { + ++_retainCount; + return self; +} +- (oneway void)release { + --_retainCount; + if (!_retainCount) + [self dealloc]; +} +- (void)dealloc { + [_ivar release]; + [super dealloc]; // no warning: different instances of same class +} +@end + +//===------------------------------------------------------------------------=== +// Do not warn about calling [super dealloc] twice if +dealloc is a class +// method. + +@interface SuperDeallocClassMethodIgnoredClass : NSObject { } ++ (void)dealloc; +@end + +@implementation SuperDeallocClassMethodIgnoredClass ++ (void)dealloc { } +@end + +@interface SuperDeallocClassMethodIgnoredSubClass : NSObject { } ++ (void)dealloc; +@end + +@implementation SuperDeallocClassMethodIgnoredSubClass ++ (void)dealloc { + [super dealloc]; + [super dealloc]; // no warning: class method +} +@end + +//===------------------------------------------------------------------------=== +// Do not warn about calling [super dealloc] twice if when the analyzer has +// inlined the call to its super deallocator. + +@interface SuperClassCallingSuperDealloc : NSObject { + NSObject *_ivar; +} +@end + +@implementation SuperClassCallingSuperDealloc +- (void)dealloc; { + [_ivar release]; // no-warning + + [super dealloc]; +} +@end + +@interface SubclassCallingSuperDealloc : SuperClassCallingSuperDealloc +@end + +@implementation SubclassCallingSuperDealloc +- (void)dealloc; { + [super dealloc]; +} +@end + +//===------------------------------------------------------------------------=== +// Treat calling [super dealloc] twice as as a sink. + +@interface CallingSuperDeallocTwiceIsSink : NSObject +@end + +@implementation CallingSuperDeallocTwiceIsSink +- (void)dealloc; { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} + // expected-note@-1 {{[super dealloc] should not be called multiple times}} + + clang_analyzer_warnIfReached(); // no-warning +} +@end + + +//===------------------------------------------------------------------------=== +// Test path notes with intervening method call on self. + +@interface InterveningMethodCallOnSelf : NSObject +@end + +@implementation InterveningMethodCallOnSelf +- (void)anotherMethod { +} + +- (void)dealloc; { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [self anotherMethod]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} + [super dealloc]; +} +@end \ No newline at end of file diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000000..f0b1798cd8 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,4 @@ +all: + clang -c DeallocUseAfterFreeErrors.m -o DeallocUseAfterFreeErrors +clean: + rm -f DeallocUseAfterFreeErrors.o \ No newline at end of file diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/Makefile b/tests/functional/quickcheck_test/quickcheck_test_files/Makefile new file mode 100644 index 0000000000..ea2c6152a0 --- /dev/null +++ b/tests/functional/quickcheck_test/quickcheck_test_files/Makefile @@ -0,0 +1,8 @@ +multi_error: + g++ -w multi_error.cpp -o /dev/null +nofail: + g++ -w nofail.cpp -o /dev/null +simple1: + g++ -w simple1.cpp -o /dev/null +simple2: + g++ -w simple2.cpp -o /dev/null diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en1.output b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en1.output index 1cec98352f..574d728857 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en1.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en1.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b 'g++ -w multi_error.cpp -o /dev/null' -e core.StackAddressEscape -d deadcode.DeadStores +CodeChecker quickcheck --analyzers clangsa -b "make multi_error" -e core.StackAddressEscape -d deadcode.DeadStores ----------------------------------------------- [INFO] - Starting build ... +g++ -w multi_error.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 1 defect(s) while analyzing multi_error.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en2.output b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en2.output index e6c91d913f..f5f6cc54ec 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en2.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.en2.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b 'g++ -w multi_error.cpp -o /dev/null' -d core.StackAddressEscape -e deadcode.DeadStores +CodeChecker quickcheck --analyzers clangsa -b "make multi_error" -d core.StackAddressEscape -e deadcode.DeadStores ----------------------------------------------- [INFO] - Starting build ... +g++ -w multi_error.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 1 defect(s) while analyzing multi_error.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.output b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.output index 6e1b5fe563..c63ad46075 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b 'g++ -w multi_error.cpp -o /dev/null' +CodeChecker quickcheck --analyzers clangsa -b "make multi_error" ----------------------------------------------- [INFO] - Starting build ... +g++ -w multi_error.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 2 defect(s) while analyzing multi_error.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.steps.output b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.steps.output index d3af8aba1b..5607392dfc 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.steps.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/multi_error.steps.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b 'g++ -w multi_error.cpp -o /dev/null' --steps +CodeChecker quickcheck --analyzers clangsa -b "make multi_error" --steps ------------------------------------------------------- [INFO] - Starting build ... +g++ -w multi_error.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 2 defect(s) while analyzing multi_error.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/nofail.output b/tests/functional/quickcheck_test/quickcheck_test_files/nofail.output index 78a3806ee2..b29a0c86ef 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/nofail.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/nofail.output @@ -1,5 +1,6 @@ -CodeChecker quickcheck --analyzers clangsa -b "g++ -w nofail.cpp -o /dev/null" +CodeChecker quickcheck --analyzers clangsa -b "make nofail" -------------------------------------------------------------------------------- [INFO] - Starting build ... +g++ -w nofail.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found no defects while analyzing nofail.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/nofail.steps.output b/tests/functional/quickcheck_test/quickcheck_test_files/nofail.steps.output index 77afae3f1b..93ab452373 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/nofail.steps.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/nofail.steps.output @@ -1,5 +1,6 @@ -CodeChecker quickcheck --analyzers clangsa -b "g++ -w nofail.cpp -o /dev/null" --steps +CodeChecker quickcheck --analyzers clangsa -b "make nofail" --steps -------------------------------------------------------------------------------- [INFO] - Starting build ... +g++ -w nofail.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found no defects while analyzing nofail.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/simple1.output b/tests/functional/quickcheck_test/quickcheck_test_files/simple1.output index 1804065d60..93557d10cf 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/simple1.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/simple1.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b "g++ -w simple1.cpp -o /dev/null" +CodeChecker quickcheck --analyzers clangsa -b "make simple1" -------------------------------------------------------------------------------- [INFO] - Starting build ... +g++ -w simple1.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 1 defect(s) while analyzing simple1.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/simple1.steps.output b/tests/functional/quickcheck_test/quickcheck_test_files/simple1.steps.output index 4ce7d5bee5..10568dc4f9 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/simple1.steps.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/simple1.steps.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b "g++ -w simple1.cpp -o /dev/null" --steps +CodeChecker quickcheck --analyzers clangsa -b "make simple1" --steps -------------------------------------------------------------------------------- [INFO] - Starting build ... +g++ -w simple1.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 1 defect(s) while analyzing simple1.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/simple2.output b/tests/functional/quickcheck_test/quickcheck_test_files/simple2.output index c59d66240e..7bba168cd4 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/simple2.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/simple2.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b "g++ -w simple2.cpp -o /dev/null" +CodeChecker quickcheck --analyzers clangsa -b "make simple2" -------------------------------------------------------------------------------- [INFO] - Starting build ... +g++ -w simple2.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 1 defect(s) while analyzing simple2.cpp diff --git a/tests/functional/quickcheck_test/quickcheck_test_files/simple2.steps.output b/tests/functional/quickcheck_test/quickcheck_test_files/simple2.steps.output index dc96399470..70b8479bf8 100644 --- a/tests/functional/quickcheck_test/quickcheck_test_files/simple2.steps.output +++ b/tests/functional/quickcheck_test/quickcheck_test_files/simple2.steps.output @@ -1,6 +1,7 @@ -CodeChecker quickcheck --analyzers clangsa -b "g++ -w simple2.cpp -o /dev/null" --steps +CodeChecker quickcheck --analyzers clangsa -b "make simple2" --steps -------------------------------------------------------------------------------- [INFO] - Starting build ... +g++ -w simple2.cpp -o /dev/null [INFO] - Build finished successfully. clangsa found 1 defect(s) while analyzing simple2.cpp diff --git a/tests/test_env_setup.sh b/tests/test_env_setup.sh old mode 100644 new mode 100755 index 61dfe20a63..51d9904a8f --- a/tests/test_env_setup.sh +++ b/tests/test_env_setup.sh @@ -1,10 +1,21 @@ -#!/usr/bin/env bash +#sd!/usr/bin/env bash -export TEST_TESTS_DIR=$(readlink -f "$(dirname "$BASH_SOURCE")") +if [[ "$OSTYPE" == "darwin"* ]]; then + export TEST_TESTS_DIR=$(greadlink -f "$(dirname "$BASH_SOURCE")") + export TEST_CODECHECKER_PACKAGE_DIR=`mktemp -d -t 'mytmpdir'` +else + export TEST_TESTS_DIR=$(readlink -f "$(dirname "$BASH_SOURCE")") + export TEST_CODECHECKER_PACKAGE_DIR=`mktemp -d` +fi -export TEST_CODECHECKER_PACKAGE_DIR=`mktemp -d` export TEST_CODECHECKER_DIR="$TEST_CODECHECKER_PACKAGE_DIR/CodeChecker" echo "Temporary directory for testing created: " $TEST_CODECHECKER_PACKAGE_DIR export TEST_CLANG_VERSION="stable" export TEST_TEST_PROJECT_CONFIG="$TEST_TESTS_DIR/test_projects/test_files/project_info.json" + +echo $TEST_TESTS_DIR +echo $TEST_CODECHECKER_PACKAGE_DIR +echo $TEST_CODECHECKER_DIR +echo $TEST_CLANG_VERSION +echo $TEST_TEST_PROJECT_CONFIG diff --git a/tests/test_projects/test_files/Makefile b/tests/test_projects/test_files/Makefile new file mode 100644 index 0000000000..8fe4c4a882 --- /dev/null +++ b/tests/test_projects/test_files/Makefile @@ -0,0 +1,14 @@ +all: + g++ -c call_and_message.cpp + g++ -c divide_zero.cpp + g++ -c new_delete.cpp + g++ -c null_dereference.cpp + g++ -c stack_address_escape.cpp + g++ -c file_to_be_skipped.cpp +clean: + rm -f call_and_message.o + rm -f divide_zero.o + rm -f new_delete.o + rm -f null_dereference.o + rm -f stack_address_escape.o + rm -f file_to_be_skipped.o diff --git a/tests/test_projects/test_files/src/call_and_message.cpp b/tests/test_projects/test_files/call_and_message.cpp similarity index 100% rename from tests/test_projects/test_files/src/call_and_message.cpp rename to tests/test_projects/test_files/call_and_message.cpp diff --git a/tests/test_projects/test_files/src/divide_zero.cpp b/tests/test_projects/test_files/divide_zero.cpp similarity index 100% rename from tests/test_projects/test_files/src/divide_zero.cpp rename to tests/test_projects/test_files/divide_zero.cpp diff --git a/tests/test_projects/test_files/src/file_to_be_skipped.cpp b/tests/test_projects/test_files/file_to_be_skipped.cpp similarity index 100% rename from tests/test_projects/test_files/src/file_to_be_skipped.cpp rename to tests/test_projects/test_files/file_to_be_skipped.cpp diff --git a/tests/test_projects/test_files/src/new_delete.cpp b/tests/test_projects/test_files/new_delete.cpp similarity index 100% rename from tests/test_projects/test_files/src/new_delete.cpp rename to tests/test_projects/test_files/new_delete.cpp diff --git a/tests/test_projects/test_files/src/null_dereference.cpp b/tests/test_projects/test_files/null_dereference.cpp similarity index 100% rename from tests/test_projects/test_files/src/null_dereference.cpp rename to tests/test_projects/test_files/null_dereference.cpp diff --git a/tests/test_projects/test_files/project_info.json b/tests/test_projects/test_files/project_info.json index e41a28a66d..7b45cf9c92 100644 --- a/tests/test_projects/test_files/project_info.json +++ b/tests/test_projects/test_files/project_info.json @@ -1,7 +1,7 @@ { "name": "test_files", - "clean_cmd": "cd src && rm -f *.o", - "build_cmd": "cd src && g++ -w -c *.cpp", + "clean_cmd": "make clean", + "build_cmd": "make", "clang_stable": { "bugs": [ { "file": "call_and_message.cpp", "line": 19, "checker": "core.CallAndMessage", "hash": "2a5c0406484f3d9606e654dadf8e6016" }, diff --git a/tests/test_projects/test_files/src/stack_address_escape.cpp b/tests/test_projects/test_files/stack_address_escape.cpp similarity index 100% rename from tests/test_projects/test_files/src/stack_address_escape.cpp rename to tests/test_projects/test_files/stack_address_escape.cpp