0
0
mirror of https://github.com/llvm/llvm-project.git synced 2025-04-21 11:46:49 +00:00

[Clang] Remove ARCMigrate ()

In the discussion around , @rjmccall mentioned that ARCMigrate
has been obsoleted and that we could go ahead and remove it from Clang,
so this patch does just that.
This commit is contained in:
Sirraide 2025-01-30 05:32:25 +01:00 committed by GitHub
parent 11026a8d8b
commit c4a019747c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
232 changed files with 28 additions and 24058 deletions
clang
CMakeLists.txt
cmake/caches
docs
include
lib
test/ARCMT

@ -432,18 +432,16 @@ if(NOT LLVM_STATIC_LINK_CXX_STDLIB)
set(HAVE_CLANG_REPL_SUPPORT ON)
endif()
option(CLANG_ENABLE_ARCMT "Build ARCMT." ON)
option(CLANG_ENABLE_OBJC_REWRITER "Build the Objective-C rewriter tool" OFF)
option(CLANG_ENABLE_STATIC_ANALYZER
"Include static analyzer in clang binary." ON)
option(CLANG_ENABLE_PROTO_FUZZER "Build Clang protobuf fuzzer." OFF)
if(NOT CLANG_ENABLE_STATIC_ANALYZER AND CLANG_ENABLE_ARCMT)
message(FATAL_ERROR "Cannot disable static analyzer while enabling ARCMT or Z3")
endif()
if(CLANG_ENABLE_ARCMT)
set(CLANG_ENABLE_OBJC_REWRITER ON)
if (DEFINED CLANG_ENABLE_ARCMT)
set(CLANG_ENABLE_OBJC_REWRITER ${CLANG_ENABLE_ARCMT})
message(DEPRECATION "'CLANG_ENABLE_ARCMT' is deprecated as ARCMigrate has been removed from Clang. Please use 'CLANG_ENABLE_OBJC_REWRITER' instead to enable or disable the Objective-C rewriter.")
endif()
# This option is a stop-gap, we should commit to removing this as

@ -2,7 +2,6 @@
set(LLVM_TARGETS_TO_BUILD X86 CACHE STRING "")
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
set(CLANG_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
set(CLANG_TIDY_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
set(CLANG_VENDOR Android CACHE STRING "")

@ -44,7 +44,6 @@ set(CLANG_DEFAULT_LINKER lld CACHE STRING "")
set(CLANG_DEFAULT_OBJCOPY llvm-objcopy CACHE STRING "")
set(CLANG_DEFAULT_RTLIB compiler-rt CACHE STRING "")
set(CLANG_DEFAULT_UNWINDLIB libunwind CACHE STRING "")
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
set(CLANG_ENABLE_STATIC_ANALYZER ON CACHE BOOL "")
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")

@ -84,7 +84,6 @@ set(CLANG_DEFAULT_LINKER lld CACHE STRING "")
set(CLANG_DEFAULT_OBJCOPY llvm-objcopy CACHE STRING "")
set(CLANG_DEFAULT_RTLIB compiler-rt CACHE STRING "")
set(CLANG_DEFAULT_UNWINDLIB libunwind CACHE STRING "")
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
set(CLANG_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")

@ -34,6 +34,8 @@ latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
Potentially Breaking Changes
============================
- The Objective-C ARC migrator (ARCMigrate) has been removed.
C/C++ Language Potentially Breaking Changes
-------------------------------------------

@ -5905,66 +5905,6 @@ CINDEX_LINKAGE const char *clang_EvalResult_getAsStr(CXEvalResult E);
* Disposes the created Eval memory.
*/
CINDEX_LINKAGE void clang_EvalResult_dispose(CXEvalResult E);
/**
* @}
*/
/** \defgroup CINDEX_REMAPPING Remapping functions
*
* @{
*/
/**
* A remapping of original source files and their translated files.
*/
typedef void *CXRemapping;
/**
* Retrieve a remapping.
*
* \param path the path that contains metadata about remappings.
*
* \returns the requested remapping. This remapping must be freed
* via a call to \c clang_remap_dispose(). Can return NULL if an error occurred.
*/
CINDEX_LINKAGE CXRemapping clang_getRemappings(const char *path);
/**
* Retrieve a remapping.
*
* \param filePaths pointer to an array of file paths containing remapping info.
*
* \param numFiles number of file paths.
*
* \returns the requested remapping. This remapping must be freed
* via a call to \c clang_remap_dispose(). Can return NULL if an error occurred.
*/
CINDEX_LINKAGE
CXRemapping clang_getRemappingsFromFileList(const char **filePaths,
unsigned numFiles);
/**
* Determine the number of remappings.
*/
CINDEX_LINKAGE unsigned clang_remap_getNumFiles(CXRemapping);
/**
* Get the original and the associated filename from the remapping.
*
* \param original If non-NULL, will be set to the original filename.
*
* \param transformed If non-NULL, will be set to the filename that the original
* is associated with.
*/
CINDEX_LINKAGE void clang_remap_getFilenames(CXRemapping, unsigned index,
CXString *original,
CXString *transformed);
/**
* Dispose the remapping.
*/
CINDEX_LINKAGE void clang_remap_dispose(CXRemapping);
/**
* @}
*/

@ -1,130 +0,0 @@
//===-- ARCMT.h - ARC Migration Rewriter ------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ARCMIGRATE_ARCMT_H
#define LLVM_CLANG_ARCMIGRATE_ARCMT_H
#include "clang/ARCMigrate/FileRemapper.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Frontend/CompilerInvocation.h"
namespace clang {
class ASTContext;
class DiagnosticConsumer;
class PCHContainerOperations;
namespace arcmt {
class MigrationPass;
/// Creates an AST with the provided CompilerInvocation but with these
/// changes:
/// -if a PCH/PTH is set, the original header is used instead
/// -Automatic Reference Counting mode is enabled
///
/// It then checks the AST and produces errors/warning for ARC migration issues
/// that the user needs to handle manually.
///
/// \param emitPremigrationARCErrors if true all ARC errors will get emitted
/// even if the migrator can fix them, but the function will still return false
/// if all ARC errors can be fixed.
///
/// \param plistOut if non-empty, it is the file path to store the plist with
/// the pre-migration ARC diagnostics.
///
/// \returns false if no error is produced, true otherwise.
bool
checkForManualIssues(CompilerInvocation &CI, const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient,
bool emitPremigrationARCErrors = false,
StringRef plistOut = StringRef());
/// Works similar to checkForManualIssues but instead of checking, it
/// applies automatic modifications to source files to conform to ARC.
///
/// \returns false if no error is produced, true otherwise.
bool
applyTransformations(CompilerInvocation &origCI,
const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient);
/// Applies automatic modifications and produces temporary files
/// and metadata into the \p outputDir path.
///
/// \param emitPremigrationARCErrors if true all ARC errors will get emitted
/// even if the migrator can fix them, but the function will still return false
/// if all ARC errors can be fixed.
///
/// \param plistOut if non-empty, it is the file path to store the plist with
/// the pre-migration ARC diagnostics.
///
/// \returns false if no error is produced, true otherwise.
bool migrateWithTemporaryFiles(
CompilerInvocation &origCI, const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient, StringRef outputDir,
bool emitPremigrationARCErrors, StringRef plistOut);
/// Get the set of file remappings from the \p outputDir path that
/// migrateWithTemporaryFiles produced.
///
/// \returns false if no error is produced, true otherwise.
bool getFileRemappings(std::vector<std::pair<std::string,std::string> > &remap,
StringRef outputDir,
DiagnosticConsumer *DiagClient);
/// Get the set of file remappings from a list of files with remapping
/// info.
///
/// \returns false if no error is produced, true otherwise.
bool getFileRemappingsFromFileList(
std::vector<std::pair<std::string,std::string> > &remap,
ArrayRef<StringRef> remapFiles,
DiagnosticConsumer *DiagClient);
typedef void (*TransformFn)(MigrationPass &pass);
std::vector<TransformFn> getAllTransformations(LangOptions::GCMode OrigGCMode,
bool NoFinalizeRemoval);
class MigrationProcess {
CompilerInvocation OrigCI;
std::shared_ptr<PCHContainerOperations> PCHContainerOps;
DiagnosticConsumer *DiagClient;
FileRemapper Remapper;
public:
bool HadARCErrors;
MigrationProcess(CompilerInvocation &CI,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *diagClient,
StringRef outputDir = StringRef());
class RewriteListener {
public:
virtual ~RewriteListener();
virtual void start(ASTContext &Ctx) { }
virtual void finish() { }
virtual void insert(SourceLocation loc, StringRef text) { }
virtual void remove(CharSourceRange range) { }
};
bool applyTransform(TransformFn trans, RewriteListener *listener = nullptr);
FileRemapper &getRemapper() { return Remapper; }
};
} // end namespace arcmt
} // end namespace clang
#endif

@ -1,76 +0,0 @@
//===--- ARCMTActions.h - ARC Migrate Tool Frontend Actions -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ARCMIGRATE_ARCMTACTIONS_H
#define LLVM_CLANG_ARCMIGRATE_ARCMTACTIONS_H
#include "clang/ARCMigrate/FileRemapper.h"
#include "clang/Frontend/FrontendAction.h"
#include <memory>
namespace clang {
namespace arcmt {
class CheckAction : public WrapperFrontendAction {
protected:
bool BeginInvocation(CompilerInstance &CI) override;
public:
CheckAction(std::unique_ptr<FrontendAction> WrappedAction);
};
class ModifyAction : public WrapperFrontendAction {
protected:
bool BeginInvocation(CompilerInstance &CI) override;
public:
ModifyAction(std::unique_ptr<FrontendAction> WrappedAction);
};
class MigrateSourceAction : public ASTFrontendAction {
FileRemapper Remapper;
protected:
bool BeginInvocation(CompilerInstance &CI) override;
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override;
};
class MigrateAction : public WrapperFrontendAction {
std::string MigrateDir;
std::string PlistOut;
bool EmitPremigrationARCErrors;
protected:
bool BeginInvocation(CompilerInstance &CI) override;
public:
MigrateAction(std::unique_ptr<FrontendAction> WrappedAction,
StringRef migrateDir,
StringRef plistOut,
bool emitPremigrationARCErrors);
};
/// Migrates to modern ObjC syntax.
class ObjCMigrateAction : public WrapperFrontendAction {
std::string MigrateDir;
unsigned ObjCMigAction;
FileRemapper Remapper;
CompilerInstance *CompInst;
public:
ObjCMigrateAction(std::unique_ptr<FrontendAction> WrappedAction,
StringRef migrateDir, unsigned migrateAction);
protected:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override;
bool BeginInvocation(CompilerInstance &CI) override;
};
}
}
#endif

@ -1,84 +0,0 @@
//===-- FileRemapper.h - File Remapping Helper ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ARCMIGRATE_FILEREMAPPER_H
#define LLVM_CLANG_ARCMIGRATE_FILEREMAPPER_H
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include <memory>
#include <variant>
namespace llvm {
class MemoryBuffer;
class MemoryBufferRef;
}
namespace clang {
class FileManager;
class DiagnosticsEngine;
class PreprocessorOptions;
namespace arcmt {
class FileRemapper {
// FIXME: Reuse the same FileManager for multiple ASTContexts.
std::unique_ptr<FileManager> FileMgr;
using Target = std::variant<FileEntryRef, llvm::MemoryBuffer *>;
using MappingsTy = llvm::DenseMap<FileEntryRef, Target>;
MappingsTy FromToMappings;
llvm::DenseMap<const FileEntry *, FileEntryRef> ToFromMappings;
public:
FileRemapper();
~FileRemapper();
bool initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
bool ignoreIfFilesChanged);
bool initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
bool ignoreIfFilesChanged);
bool flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag);
bool flushToFile(StringRef outputPath, DiagnosticsEngine &Diag);
bool overwriteOriginal(DiagnosticsEngine &Diag,
StringRef outputDir = StringRef());
void remap(StringRef filePath, std::unique_ptr<llvm::MemoryBuffer> memBuf);
void applyMappings(PreprocessorOptions &PPOpts) const;
/// Iterate through all the mappings.
void forEachMapping(
llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
CaptureBuffer) const;
void clear(StringRef outputDir = StringRef());
private:
void remap(FileEntryRef file, std::unique_ptr<llvm::MemoryBuffer> memBuf);
void remap(FileEntryRef file, FileEntryRef newfile);
OptionalFileEntryRef getOriginalFile(StringRef filePath);
void resetTarget(Target &targ);
bool report(const Twine &err, DiagnosticsEngine &Diag);
std::string getRemapInfoFile(StringRef outputDir);
};
} // end namespace arcmt
} // end namespace clang
#endif

@ -402,16 +402,6 @@ def note_file_misc_sloc_usage : Note<
def err_module_format_unhandled : Error<
"no handler registered for module format '%0'">, DefaultFatal;
// TransformActions
// TODO: Use a custom category name to distinguish rewriter errors.
def err_mt_message : Error<"[rewriter] %0">, SuppressInSystemHeader;
def warn_mt_message : Warning<"[rewriter] %0">;
def note_mt_message : Note<"[rewriter] %0">;
// ARCMigrate
def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">;
def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">;
// API notes
def err_apinotes_message : Error<"%0">;
def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>;

@ -76,7 +76,6 @@
#cmakedefine01 PPC_LINUX_DEFAULT_IEEELONGDOUBLE
/* Enable each functionality of modules */
#cmakedefine01 CLANG_ENABLE_ARCMT
#cmakedefine01 CLANG_ENABLE_OBJC_REWRITER
#cmakedefine01 CLANG_ENABLE_STATIC_ANALYZER

@ -60,7 +60,6 @@ public:
PrecompileJobClass,
ExtractAPIJobClass,
AnalyzeJobClass,
MigrateJobClass,
CompileJobClass,
BackendJobClass,
AssembleJobClass,
@ -460,17 +459,6 @@ public:
}
};
class MigrateJobAction : public JobAction {
void anchor() override;
public:
MigrateJobAction(Action *Input, types::ID OutputType);
static bool classof(const Action *A) {
return A->getKind() == MigrateJobClass;
}
};
class CompileJobAction : public JobAction {
void anchor() override;

@ -694,20 +694,6 @@ def ccc_print_phases : Flag<["-"], "ccc-print-phases">,
def ccc_print_bindings : Flag<["-"], "ccc-print-bindings">, InternalDebugOpt,
HelpText<"Show bindings of tools to actions">;
def ccc_arcmt_check : Flag<["-"], "ccc-arcmt-check">, InternalDriverOpt,
HelpText<"Check for ARC migration issues that need manual handling">;
def ccc_arcmt_modify : Flag<["-"], "ccc-arcmt-modify">, InternalDriverOpt,
HelpText<"Apply modifications to files to conform to ARC">;
def ccc_arcmt_migrate : Separate<["-"], "ccc-arcmt-migrate">, InternalDriverOpt,
HelpText<"Apply modifications and produces temporary files that conform to ARC">;
def arcmt_migrate_report_output : Separate<["-"], "arcmt-migrate-report-output">,
HelpText<"Output path for the plist report">,
Visibility<[ClangOption, CC1Option]>,
MarshallingInfoString<FrontendOpts<"ARCMTMigrateReportOut">>;
def arcmt_migrate_emit_arc_errors : Flag<["-"], "arcmt-migrate-emit-errors">,
HelpText<"Emit ARC errors even if the migrator can fix them">,
Visibility<[ClangOption, CC1Option]>,
MarshallingInfoFlag<FrontendOpts<"ARCMTMigrateEmitARCErrors">>;
def gen_reproducer_eq: Joined<["-"], "gen-reproducer=">,
Flags<[NoArgumentUnused]>, Visibility<[ClangOption, CLOption, DXCOption]>,
HelpText<"Emit reproducer on (option: off, crash (default), error, always)">;
@ -723,87 +709,6 @@ def no_round_trip_args : Flag<["-"], "no-round-trip-args">,
Visibility<[CC1Option]>,
HelpText<"Disable command line arguments round-trip.">;
def _migrate : Flag<["--"], "migrate">, Flags<[NoXarchOption]>,
HelpText<"Run the migrator">;
def ccc_objcmt_migrate : Separate<["-"], "ccc-objcmt-migrate">,
InternalDriverOpt,
HelpText<"Apply modifications and produces temporary files to migrate to "
"modern ObjC syntax">;
def objcmt_migrate_literals : Flag<["-"], "objcmt-migrate-literals">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to modern ObjC literals">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_Literals">;
def objcmt_migrate_subscripting : Flag<["-"], "objcmt-migrate-subscripting">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to modern ObjC subscripting">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_Subscripting">;
def objcmt_migrate_property : Flag<["-"], "objcmt-migrate-property">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to modern ObjC property">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_Property">;
def objcmt_migrate_all : Flag<["-"], "objcmt-migrate-all">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to modern ObjC">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_MigrateDecls">;
def objcmt_migrate_readonly_property : Flag<["-"], "objcmt-migrate-readonly-property">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to modern ObjC readonly property">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_ReadonlyProperty">;
def objcmt_migrate_readwrite_property : Flag<["-"], "objcmt-migrate-readwrite-property">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to modern ObjC readwrite property">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_ReadwriteProperty">;
def objcmt_migrate_property_dot_syntax : Flag<["-"], "objcmt-migrate-property-dot-syntax">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration of setter/getter messages to property-dot syntax">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_PropertyDotSyntax">;
def objcmt_migrate_annotation : Flag<["-"], "objcmt-migrate-annotation">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to property and method annotations">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_Annotation">;
def objcmt_migrate_instancetype : Flag<["-"], "objcmt-migrate-instancetype">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to infer instancetype for method result type">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_Instancetype">;
def objcmt_migrate_nsmacros : Flag<["-"], "objcmt-migrate-ns-macros">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to NS_ENUM/NS_OPTIONS macros">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_NsMacros">;
def objcmt_migrate_protocol_conformance : Flag<["-"], "objcmt-migrate-protocol-conformance">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to add protocol conformance on classes">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_ProtocolConformance">;
def objcmt_atomic_property : Flag<["-"], "objcmt-atomic-property">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Make migration to 'atomic' properties">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_AtomicProperty">;
def objcmt_returns_innerpointer_property : Flag<["-"], "objcmt-returns-innerpointer-property">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to annotate property with NS_RETURNS_INNER_POINTER">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_ReturnsInnerPointerProperty">;
def objcmt_ns_nonatomic_iosonly: Flag<["-"], "objcmt-ns-nonatomic-iosonly">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to use NS_NONATOMIC_IOSONLY macro for setting property's 'atomic' attribute">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty">;
def objcmt_migrate_designated_init : Flag<["-"], "objcmt-migrate-designated-init">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable migration to infer NS_DESIGNATED_INITIALIZER for initializer methods">,
MarshallingInfoBitfieldFlag<FrontendOpts<"ObjCMTAction">, "FrontendOptions::ObjCMT_DesignatedInitializer">;
def objcmt_allowlist_dir_path: Joined<["-"], "objcmt-allowlist-dir-path=">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Only modify files with a filename contained in the provided directory path">,
MarshallingInfoString<FrontendOpts<"ObjCMTAllowListPath">>;
def : Joined<["-"], "objcmt-whitelist-dir-path=">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Alias for -objcmt-allowlist-dir-path">,
Alias<objcmt_allowlist_dir_path>;
// The misspelt "white-list" [sic] alias is due for removal.
def : Joined<["-"], "objcmt-white-list-dir-path=">,
Visibility<[ClangOption, CC1Option]>,
Alias<objcmt_allowlist_dir_path>;
// Make sure all other -ccc- options are rejected.
def ccc_ : Joined<["-"], "ccc-">, Group<internal_Group>, Flags<[Unsupported]>;
@ -7975,8 +7880,6 @@ def rewrite_test : Flag<["-"], "rewrite-test">,
HelpText<"Rewriter playground">;
def rewrite_macros : Flag<["-"], "rewrite-macros">,
HelpText<"Expand macros without full preprocessing">;
def migrate : Flag<["-"], "migrate">,
HelpText<"Migrate source code">;
def compiler_options_dump : Flag<["-"], "compiler-options-dump">,
HelpText<"Dump the compiler configuration options">;
def print_dependency_directives_minimized_source : Flag<["-"],
@ -7990,17 +7893,6 @@ defm emit_llvm_uselists : BoolOption<"", "emit-llvm-uselists",
NegFlag<SetFalse, [], [ClangOption], "Don't preserve">,
BothFlags<[], [ClangOption], " order of LLVM use-lists when serializing">>;
def mt_migrate_directory : Separate<["-"], "mt-migrate-directory">,
HelpText<"Directory for temporary files produced during ARC or ObjC migration">,
MarshallingInfoString<FrontendOpts<"MTMigrateDir">>;
def arcmt_action_EQ : Joined<["-"], "arcmt-action=">, Visibility<[CC1Option]>,
HelpText<"The ARC migration action to take">,
Values<"check,modify,migrate">,
NormalizedValuesScope<"FrontendOptions">,
NormalizedValues<["ARCMT_Check", "ARCMT_Modify", "ARCMT_Migrate"]>,
MarshallingInfoEnum<FrontendOpts<"ARCMTAction">, "ARCMT_None">;
def print_stats : Flag<["-"], "print-stats">,
HelpText<"Print performance metrics and statistics">,
MarshallingInfoFlag<FrontendOpts<"ShowStats">>;

@ -141,9 +141,6 @@ enum ActionKind {
/// Dump template instantiations
TemplightDump,
/// Run migrator.
MigrateSource,
/// Just lex, no output.
RunPreprocessorOnly,
@ -330,10 +327,6 @@ public:
LLVM_PREFERRED_TYPE(bool)
unsigned FixToTemporaries : 1;
/// Emit ARC errors even if the migrator can fix them.
LLVM_PREFERRED_TYPE(bool)
unsigned ARCMTMigrateEmitARCErrors : 1;
/// Skip over function bodies to speed up parsing in cases you do not need
/// them (e.g. with code completion).
LLVM_PREFERRED_TYPE(bool)
@ -424,72 +417,6 @@ public:
/// Specifies the output format of the AST.
ASTDumpOutputFormat ASTDumpFormat = ADOF_Default;
enum {
ARCMT_None,
ARCMT_Check,
ARCMT_Modify,
ARCMT_Migrate
} ARCMTAction = ARCMT_None;
enum {
ObjCMT_None = 0,
/// Enable migration to modern ObjC literals.
ObjCMT_Literals = 0x1,
/// Enable migration to modern ObjC subscripting.
ObjCMT_Subscripting = 0x2,
/// Enable migration to modern ObjC readonly property.
ObjCMT_ReadonlyProperty = 0x4,
/// Enable migration to modern ObjC readwrite property.
ObjCMT_ReadwriteProperty = 0x8,
/// Enable migration to modern ObjC property.
ObjCMT_Property = (ObjCMT_ReadonlyProperty | ObjCMT_ReadwriteProperty),
/// Enable annotation of ObjCMethods of all kinds.
ObjCMT_Annotation = 0x10,
/// Enable migration of ObjC methods to 'instancetype'.
ObjCMT_Instancetype = 0x20,
/// Enable migration to NS_ENUM/NS_OPTIONS macros.
ObjCMT_NsMacros = 0x40,
/// Enable migration to add conforming protocols.
ObjCMT_ProtocolConformance = 0x80,
/// prefer 'atomic' property over 'nonatomic'.
ObjCMT_AtomicProperty = 0x100,
/// annotate property with NS_RETURNS_INNER_POINTER
ObjCMT_ReturnsInnerPointerProperty = 0x200,
/// use NS_NONATOMIC_IOSONLY for property 'atomic' attribute
ObjCMT_NsAtomicIOSOnlyProperty = 0x400,
/// Enable inferring NS_DESIGNATED_INITIALIZER for ObjC methods.
ObjCMT_DesignatedInitializer = 0x800,
/// Enable converting setter/getter expressions to property-dot syntx.
ObjCMT_PropertyDotSyntax = 0x1000,
ObjCMT_MigrateDecls = (ObjCMT_ReadonlyProperty | ObjCMT_ReadwriteProperty |
ObjCMT_Annotation | ObjCMT_Instancetype |
ObjCMT_NsMacros | ObjCMT_ProtocolConformance |
ObjCMT_NsAtomicIOSOnlyProperty |
ObjCMT_DesignatedInitializer),
ObjCMT_MigrateAll = (ObjCMT_Literals | ObjCMT_Subscripting |
ObjCMT_MigrateDecls | ObjCMT_PropertyDotSyntax)
};
unsigned ObjCMTAction = ObjCMT_None;
std::string ObjCMTAllowListPath;
std::string MTMigrateDir;
std::string ARCMTMigrateReportOut;
/// The input kind, either specified via -x argument or deduced from the input
/// file name.
InputKind DashX;
@ -596,14 +523,14 @@ public:
: DisableFree(false), RelocatablePCH(false), ShowHelp(false),
ShowStats(false), AppendStats(false), ShowVersion(false),
FixWhatYouCan(false), FixOnlyWarnings(false), FixAndRecompile(false),
FixToTemporaries(false), ARCMTMigrateEmitARCErrors(false),
SkipFunctionBodies(false), UseGlobalModuleIndex(true),
GenerateGlobalModuleIndex(true), ASTDumpDecls(false),
ASTDumpLookups(false), BuildingImplicitModule(false),
BuildingImplicitModuleUsesLock(true), ModulesEmbedAllFiles(false),
IncludeTimestamps(true), UseTemporary(true),
AllowPCMWithCompilerErrors(false), ModulesShareFileManager(true),
EmitSymbolGraph(false), EmitExtensionSymbolGraphs(false),
FixToTemporaries(false), SkipFunctionBodies(false),
UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true),
ASTDumpDecls(false), ASTDumpLookups(false),
BuildingImplicitModule(false), BuildingImplicitModuleUsesLock(true),
ModulesEmbedAllFiles(false), IncludeTimestamps(true),
UseTemporary(true), AllowPCMWithCompilerErrors(false),
ModulesShareFileManager(true), EmitSymbolGraph(false),
EmitExtensionSymbolGraphs(false),
EmitSymbolGraphSymbolLabelsForTesting(false),
EmitPrettySymbolGraphs(false), GenReducedBMI(false),
UseClangIRPipeline(false), TimeTraceGranularity(500),

@ -1,616 +0,0 @@
//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/ARCMigrate/ARCMT.h"
#include "Internals.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/DiagnosticCategories.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Serialization/ASTReader.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/TargetParser/Triple.h"
#include <utility>
using namespace clang;
using namespace arcmt;
bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
SourceRange range) {
if (range.isInvalid())
return false;
bool cleared = false;
ListTy::iterator I = List.begin();
while (I != List.end()) {
FullSourceLoc diagLoc = I->getLocation();
if ((IDs.empty() || // empty means clear all diagnostics in the range.
llvm::is_contained(IDs, I->getID())) &&
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
(diagLoc == range.getEnd() ||
diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
cleared = true;
ListTy::iterator eraseS = I++;
if (eraseS->getLevel() != DiagnosticsEngine::Note)
while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
++I;
// Clear the diagnostic and any notes following it.
I = List.erase(eraseS, I);
continue;
}
++I;
}
return cleared;
}
bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
SourceRange range) const {
if (range.isInvalid())
return false;
ListTy::const_iterator I = List.begin();
while (I != List.end()) {
FullSourceLoc diagLoc = I->getLocation();
if ((IDs.empty() || // empty means any diagnostic in the range.
llvm::is_contained(IDs, I->getID())) &&
!diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
(diagLoc == range.getEnd() ||
diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
return true;
}
++I;
}
return false;
}
void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
Diags.Report(*I);
}
bool CapturedDiagList::hasErrors() const {
for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
if (I->getLevel() >= DiagnosticsEngine::Error)
return true;
return false;
}
namespace {
class CaptureDiagnosticConsumer : public DiagnosticConsumer {
DiagnosticsEngine &Diags;
DiagnosticConsumer &DiagClient;
CapturedDiagList &CapturedDiags;
bool HasBegunSourceFile;
public:
CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
DiagnosticConsumer &client,
CapturedDiagList &capturedDiags)
: Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
HasBegunSourceFile(false) { }
void BeginSourceFile(const LangOptions &Opts,
const Preprocessor *PP) override {
// Pass BeginSourceFile message onto DiagClient on first call.
// The corresponding EndSourceFile call will be made from an
// explicit call to FinishCapture.
if (!HasBegunSourceFile) {
DiagClient.BeginSourceFile(Opts, PP);
HasBegunSourceFile = true;
}
}
void FinishCapture() {
// Call EndSourceFile on DiagClient on completion of capture to
// enable VerifyDiagnosticConsumer to check diagnostics *after*
// it has received the diagnostic list.
if (HasBegunSourceFile) {
DiagClient.EndSourceFile();
HasBegunSourceFile = false;
}
}
~CaptureDiagnosticConsumer() override {
assert(!HasBegunSourceFile && "FinishCapture not called!");
}
void HandleDiagnostic(DiagnosticsEngine::Level level,
const Diagnostic &Info) override {
if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
if (Info.getLocation().isValid())
CapturedDiags.push_back(StoredDiagnostic(level, Info));
return;
}
// Non-ARC warnings are ignored.
Diags.setLastDiagnosticIgnored(true);
}
};
} // end anonymous namespace
static bool HasARCRuntime(CompilerInvocation &origCI) {
// This duplicates some functionality from Darwin::AddDeploymentTarget
// but this function is well defined, so keep it decoupled from the driver
// and avoid unrelated complications.
llvm::Triple triple(origCI.getTargetOpts().Triple);
if (triple.isiOS())
return triple.getOSMajorVersion() >= 5;
if (triple.isWatchOS())
return true;
if (triple.getOS() == llvm::Triple::Darwin)
return triple.getOSMajorVersion() >= 11;
if (triple.getOS() == llvm::Triple::MacOSX) {
return triple.getOSVersion() >= VersionTuple(10, 7);
}
return false;
}
static CompilerInvocation *
createInvocationForMigration(CompilerInvocation &origCI,
const PCHContainerReader &PCHContainerRdr) {
std::unique_ptr<CompilerInvocation> CInvok;
CInvok.reset(new CompilerInvocation(origCI));
PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts();
if (!PPOpts.ImplicitPCHInclude.empty()) {
// We can't use a PCH because it was likely built in non-ARC mode and we
// want to parse in ARC. Include the original header.
FileManager FileMgr(origCI.getFileSystemOpts());
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
new IgnoringDiagConsumer()));
std::string OriginalFile = ASTReader::getOriginalSourceFile(
PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags);
if (!OriginalFile.empty())
PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile);
PPOpts.ImplicitPCHInclude.clear();
}
std::string define = std::string(getARCMTMacroName());
define += '=';
CInvok->getPreprocessorOpts().addMacroDef(define);
CInvok->getLangOpts().ObjCAutoRefCount = true;
CInvok->getLangOpts().setGC(LangOptions::NonGC);
CInvok->getDiagnosticOpts().ErrorLimit = 0;
CInvok->getDiagnosticOpts().PedanticErrors = 0;
// Ignore -Werror flags when migrating.
std::vector<std::string> WarnOpts;
for (std::vector<std::string>::iterator
I = CInvok->getDiagnosticOpts().Warnings.begin(),
E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
if (!StringRef(*I).starts_with("error"))
WarnOpts.push_back(*I);
}
WarnOpts.push_back("error=arc-unsafe-retained-assign");
CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts);
CInvok->getLangOpts().ObjCWeakRuntime = HasARCRuntime(origCI);
CInvok->getLangOpts().ObjCWeak = CInvok->getLangOpts().ObjCWeakRuntime;
return CInvok.release();
}
static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
DiagnosticOptions *diagOpts,
Preprocessor &PP) {
TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, diagOpts, &printer,
/*ShouldOwnClient=*/false));
Diags->setSourceManager(&PP.getSourceManager());
printer.BeginSourceFile(PP.getLangOpts(), &PP);
arcDiags.reportDiagnostics(*Diags);
printer.EndSourceFile();
}
//===----------------------------------------------------------------------===//
// checkForManualIssues.
//===----------------------------------------------------------------------===//
bool arcmt::checkForManualIssues(
CompilerInvocation &origCI, const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors,
StringRef plistOut) {
if (!origCI.getLangOpts().ObjC)
return false;
LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC();
bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
NoFinalizeRemoval);
assert(!transforms.empty());
std::unique_ptr<CompilerInvocation> CInvok;
CInvok.reset(
createInvocationForMigration(origCI, PCHContainerOps->getRawReader()));
CInvok->getFrontendOpts().Inputs.clear();
CInvok->getFrontendOpts().Inputs.push_back(Input);
CapturedDiagList capturedDiags;
assert(DiagClient);
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
DiagClient, /*ShouldOwnClient=*/false));
// Filter of all diagnostics.
CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
std::move(CInvok), PCHContainerOps, Diags));
if (!Unit) {
errRec.FinishCapture();
return true;
}
// Don't filter diagnostics anymore.
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
ASTContext &Ctx = Unit->getASTContext();
if (Diags->hasFatalErrorOccurred()) {
Diags->Reset();
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
capturedDiags.reportDiagnostics(*Diags);
DiagClient->EndSourceFile();
errRec.FinishCapture();
return true;
}
if (emitPremigrationARCErrors)
emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(),
Unit->getPreprocessor());
if (!plistOut.empty()) {
SmallVector<StoredDiagnostic, 8> arcDiags;
for (CapturedDiagList::iterator
I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
arcDiags.push_back(*I);
writeARCDiagsToPlist(std::string(plistOut), arcDiags,
Ctx.getSourceManager(), Ctx.getLangOpts());
}
// After parsing of source files ended, we want to reuse the
// diagnostics objects to emit further diagnostics.
// We call BeginSourceFile because DiagnosticConsumer requires that
// diagnostics with source range information are emitted only in between
// BeginSourceFile() and EndSourceFile().
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
// No macros will be added since we are just checking and we won't modify
// source code.
std::vector<SourceLocation> ARCMTMacroLocs;
TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags,
ARCMTMacroLocs);
pass.setNoFinalizeRemoval(NoFinalizeRemoval);
if (!NoNSAllocReallocError)
Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error,
SourceLocation());
for (unsigned i=0, e = transforms.size(); i != e; ++i)
transforms[i](pass);
capturedDiags.reportDiagnostics(*Diags);
DiagClient->EndSourceFile();
errRec.FinishCapture();
return capturedDiags.hasErrors() || testAct.hasReportedErrors();
}
//===----------------------------------------------------------------------===//
// applyTransformations.
//===----------------------------------------------------------------------===//
static bool
applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient, StringRef outputDir,
bool emitPremigrationARCErrors, StringRef plistOut) {
if (!origCI.getLangOpts().ObjC)
return false;
LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC();
// Make sure checking is successful first.
CompilerInvocation CInvokForCheck(origCI);
if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps,
DiagClient, emitPremigrationARCErrors,
plistOut))
return true;
CompilerInvocation CInvok(origCI);
CInvok.getFrontendOpts().Inputs.clear();
CInvok.getFrontendOpts().Inputs.push_back(Input);
MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir);
bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
NoFinalizeRemoval);
assert(!transforms.empty());
for (unsigned i=0, e = transforms.size(); i != e; ++i) {
bool err = migration.applyTransform(transforms[i]);
if (err) return true;
}
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(),
DiagClient, /*ShouldOwnClient=*/false));
if (outputDir.empty()) {
origCI.getLangOpts().ObjCAutoRefCount = true;
return migration.getRemapper().overwriteOriginal(*Diags);
} else {
return migration.getRemapper().flushToDisk(outputDir, *Diags);
}
}
bool arcmt::applyTransformations(
CompilerInvocation &origCI, const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient) {
return applyTransforms(origCI, Input, PCHContainerOps, DiagClient,
StringRef(), false, StringRef());
}
bool arcmt::migrateWithTemporaryFiles(
CompilerInvocation &origCI, const FrontendInputFile &Input,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagClient, StringRef outputDir,
bool emitPremigrationARCErrors, StringRef plistOut) {
assert(!outputDir.empty() && "Expected output directory path");
return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir,
emitPremigrationARCErrors, plistOut);
}
bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
remap,
StringRef outputDir,
DiagnosticConsumer *DiagClient) {
assert(!outputDir.empty());
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, new DiagnosticOptions,
DiagClient, /*ShouldOwnClient=*/false));
FileRemapper remapper;
bool err = remapper.initFromDisk(outputDir, *Diags,
/*ignoreIfFilesChanged=*/true);
if (err)
return true;
remapper.forEachMapping(
[&](StringRef From, StringRef To) {
remap.push_back(std::make_pair(From.str(), To.str()));
},
[](StringRef, const llvm::MemoryBufferRef &) {});
return false;
}
//===----------------------------------------------------------------------===//
// CollectTransformActions.
//===----------------------------------------------------------------------===//
namespace {
class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
std::vector<SourceLocation> &ARCMTMacroLocs;
public:
ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
: ARCMTMacroLocs(ARCMTMacroLocs) { }
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range, const MacroArgs *Args) override {
if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
}
};
class ARCMTMacroTrackerAction : public ASTFrontendAction {
std::vector<SourceLocation> &ARCMTMacroLocs;
public:
ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
: ARCMTMacroLocs(ARCMTMacroLocs) { }
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override {
CI.getPreprocessor().addPPCallbacks(
std::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
return std::make_unique<ASTConsumer>();
}
};
class RewritesApplicator : public TransformActions::RewriteReceiver {
Rewriter &rewriter;
MigrationProcess::RewriteListener *Listener;
public:
RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
MigrationProcess::RewriteListener *listener)
: rewriter(rewriter), Listener(listener) {
if (Listener)
Listener->start(ctx);
}
~RewritesApplicator() override {
if (Listener)
Listener->finish();
}
void insert(SourceLocation loc, StringRef text) override {
bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
/*indentNewLines=*/true);
if (!err && Listener)
Listener->insert(loc, text);
}
void remove(CharSourceRange range) override {
Rewriter::RewriteOptions removeOpts;
removeOpts.IncludeInsertsAtBeginOfRange = false;
removeOpts.IncludeInsertsAtEndOfRange = false;
removeOpts.RemoveLineIfEmpty = true;
bool err = rewriter.RemoveText(range, removeOpts);
if (!err && Listener)
Listener->remove(range);
}
void increaseIndentation(CharSourceRange range,
SourceLocation parentIndent) override {
rewriter.IncreaseIndentation(range, parentIndent);
}
};
} // end anonymous namespace.
/// Anchor for VTable.
MigrationProcess::RewriteListener::~RewriteListener() { }
MigrationProcess::MigrationProcess(
CompilerInvocation &CI,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *diagClient, StringRef outputDir)
: OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)),
DiagClient(diagClient), HadARCErrors(false) {
if (!outputDir.empty()) {
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(),
DiagClient, /*ShouldOwnClient=*/false));
Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanged=*/true);
}
}
bool MigrationProcess::applyTransform(TransformFn trans,
RewriteListener *listener) {
std::unique_ptr<CompilerInvocation> CInvok;
CInvok.reset(
createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader()));
CInvok->getDiagnosticOpts().IgnoreWarnings = true;
Remapper.applyMappings(CInvok->getPreprocessorOpts());
CapturedDiagList capturedDiags;
std::vector<SourceLocation> ARCMTMacroLocs;
assert(DiagClient);
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
new DiagnosticsEngine(DiagID, new DiagnosticOptions,
DiagClient, /*ShouldOwnClient=*/false));
// Filter of all diagnostics.
CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
std::unique_ptr<ARCMTMacroTrackerAction> ASTAction;
ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
std::move(CInvok), PCHContainerOps, Diags, ASTAction.get()));
if (!Unit) {
errRec.FinishCapture();
return true;
}
Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
HadARCErrors = HadARCErrors || capturedDiags.hasErrors();
// Don't filter diagnostics anymore.
Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
ASTContext &Ctx = Unit->getASTContext();
if (Diags->hasFatalErrorOccurred()) {
Diags->Reset();
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
capturedDiags.reportDiagnostics(*Diags);
DiagClient->EndSourceFile();
errRec.FinishCapture();
return true;
}
// After parsing of source files ended, we want to reuse the
// diagnostics objects to emit further diagnostics.
// We call BeginSourceFile because DiagnosticConsumer requires that
// diagnostics with source range information are emitted only in between
// BeginSourceFile() and EndSourceFile().
DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
MigrationPass pass(Ctx, OrigCI.getLangOpts().getGC(),
Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs);
trans(pass);
{
RewritesApplicator applicator(rewriter, Ctx, listener);
TA.applyRewrites(applicator);
}
DiagClient->EndSourceFile();
errRec.FinishCapture();
if (DiagClient->getNumErrors())
return true;
for (Rewriter::buffer_iterator
I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
FileID FID = I->first;
llvm::RewriteBuffer &buf = I->second;
OptionalFileEntryRef file =
Ctx.getSourceManager().getFileEntryRefForID(FID);
assert(file);
std::string newFname = std::string(file->getName());
newFname += "-trans";
SmallString<512> newText;
llvm::raw_svector_ostream vecOS(newText);
buf.write(vecOS);
std::unique_ptr<llvm::MemoryBuffer> memBuf(
llvm::MemoryBuffer::getMemBufferCopy(newText.str(), newFname));
SmallString<64> filePath(file->getName());
Unit->getFileManager().FixupRelativePath(filePath);
Remapper.remap(filePath.str(), std::move(memBuf));
}
return false;
}

@ -1,59 +0,0 @@
//===--- ARCMTActions.cpp - ARC Migrate Tool Frontend Actions ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/ARCMigrate/ARCMTActions.h"
#include "clang/ARCMigrate/ARCMT.h"
#include "clang/Frontend/CompilerInstance.h"
using namespace clang;
using namespace arcmt;
bool CheckAction::BeginInvocation(CompilerInstance &CI) {
if (arcmt::checkForManualIssues(CI.getInvocation(), getCurrentInput(),
CI.getPCHContainerOperations(),
CI.getDiagnostics().getClient()))
return false; // errors, stop the action.
// We only want to see warnings reported from arcmt::checkForManualIssues.
CI.getDiagnostics().setIgnoreAllWarnings(true);
return true;
}
CheckAction::CheckAction(std::unique_ptr<FrontendAction> WrappedAction)
: WrapperFrontendAction(std::move(WrappedAction)) {}
bool ModifyAction::BeginInvocation(CompilerInstance &CI) {
return !arcmt::applyTransformations(CI.getInvocation(), getCurrentInput(),
CI.getPCHContainerOperations(),
CI.getDiagnostics().getClient());
}
ModifyAction::ModifyAction(std::unique_ptr<FrontendAction> WrappedAction)
: WrapperFrontendAction(std::move(WrappedAction)) {}
bool MigrateAction::BeginInvocation(CompilerInstance &CI) {
if (arcmt::migrateWithTemporaryFiles(
CI.getInvocation(), getCurrentInput(), CI.getPCHContainerOperations(),
CI.getDiagnostics().getClient(), MigrateDir, EmitPremigrationARCErrors,
PlistOut))
return false; // errors, stop the action.
// We only want to see diagnostics emitted by migrateWithTemporaryFiles.
CI.getDiagnostics().setIgnoreAllWarnings(true);
return true;
}
MigrateAction::MigrateAction(std::unique_ptr<FrontendAction> WrappedAction,
StringRef migrateDir,
StringRef plistOut,
bool emitPremigrationARCErrors)
: WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),
PlistOut(plistOut), EmitPremigrationARCErrors(emitPremigrationARCErrors) {
if (MigrateDir.empty())
MigrateDir = "."; // user current directory if none is given.
}

@ -1,48 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
TargetParser
)
# By default MSVC has a 2^16 limit on the number of sections in an object
# file, and Transforms.cpp needs more than that.
if (MSVC)
set_source_files_properties(Transforms.cpp PROPERTIES COMPILE_FLAGS /bigobj)
endif()
add_clang_library(clangARCMigrate
ARCMT.cpp
ARCMTActions.cpp
FileRemapper.cpp
ObjCMT.cpp
PlistReporter.cpp
TransAPIUses.cpp
TransARCAssign.cpp
TransAutoreleasePool.cpp
TransBlockObjCVariable.cpp
TransEmptyStatementsAndDealloc.cpp
TransGCAttrs.cpp
TransGCCalls.cpp
TransProperties.cpp
TransProtectedScope.cpp
TransRetainReleaseDealloc.cpp
TransUnbridgedCasts.cpp
TransUnusedInitDelegate.cpp
TransZeroOutPropsInDealloc.cpp
TransformActions.cpp
Transforms.cpp
LINK_LIBS
clangAST
clangAnalysis
clangBasic
clangEdit
clangFrontend
clangLex
clangRewrite
clangSema
clangSerialization
DEPENDS
omp_gen
ClangDriverOptions
)

@ -1,274 +0,0 @@
//===--- FileRemapper.cpp - File Remapping Helper -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/ARCMigrate/FileRemapper.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
using namespace clang;
using namespace arcmt;
FileRemapper::FileRemapper() {
FileMgr.reset(new FileManager(FileSystemOptions()));
}
FileRemapper::~FileRemapper() {
clear();
}
void FileRemapper::clear(StringRef outputDir) {
for (MappingsTy::iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I)
resetTarget(I->second);
FromToMappings.clear();
assert(ToFromMappings.empty());
if (!outputDir.empty()) {
std::string infoFile = getRemapInfoFile(outputDir);
llvm::sys::fs::remove(infoFile);
}
}
std::string FileRemapper::getRemapInfoFile(StringRef outputDir) {
assert(!outputDir.empty());
SmallString<128> InfoFile = outputDir;
llvm::sys::path::append(InfoFile, "remap");
return std::string(InfoFile);
}
bool FileRemapper::initFromDisk(StringRef outputDir, DiagnosticsEngine &Diag,
bool ignoreIfFilesChanged) {
std::string infoFile = getRemapInfoFile(outputDir);
return initFromFile(infoFile, Diag, ignoreIfFilesChanged);
}
bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag,
bool ignoreIfFilesChanged) {
assert(FromToMappings.empty() &&
"initFromDisk should be called before any remap calls");
std::string infoFile = std::string(filePath);
if (!llvm::sys::fs::exists(infoFile))
return false;
std::vector<std::pair<FileEntryRef, FileEntryRef>> pairs;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBuf =
llvm::MemoryBuffer::getFile(infoFile, /*IsText=*/true);
if (!fileBuf)
return report("Error opening file: " + infoFile, Diag);
SmallVector<StringRef, 64> lines;
fileBuf.get()->getBuffer().split(lines, "\n");
for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) {
StringRef fromFilename = lines[idx];
unsigned long long timeModified;
if (lines[idx+1].getAsInteger(10, timeModified))
return report("Invalid file data: '" + lines[idx+1] + "' not a number",
Diag);
StringRef toFilename = lines[idx+2];
auto origFE = FileMgr->getOptionalFileRef(fromFilename);
if (!origFE) {
if (ignoreIfFilesChanged)
continue;
return report("File does not exist: " + fromFilename, Diag);
}
auto newFE = FileMgr->getOptionalFileRef(toFilename);
if (!newFE) {
if (ignoreIfFilesChanged)
continue;
return report("File does not exist: " + toFilename, Diag);
}
if ((uint64_t)origFE->getModificationTime() != timeModified) {
if (ignoreIfFilesChanged)
continue;
return report("File was modified: " + fromFilename, Diag);
}
pairs.push_back(std::make_pair(*origFE, *newFE));
}
for (unsigned i = 0, e = pairs.size(); i != e; ++i)
remap(pairs[i].first, pairs[i].second);
return false;
}
bool FileRemapper::flushToDisk(StringRef outputDir, DiagnosticsEngine &Diag) {
using namespace llvm::sys;
if (fs::create_directory(outputDir))
return report("Could not create directory: " + outputDir, Diag);
std::string infoFile = getRemapInfoFile(outputDir);
return flushToFile(infoFile, Diag);
}
bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) {
using namespace llvm::sys;
std::error_code EC;
std::string infoFile = std::string(outputPath);
llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_Text);
if (EC)
return report(EC.message(), Diag);
for (MappingsTy::iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
FileEntryRef origFE = I->first;
SmallString<200> origPath = StringRef(origFE.getName());
fs::make_absolute(origPath);
infoOut << origPath << '\n';
infoOut << (uint64_t)origFE.getModificationTime() << '\n';
if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) {
SmallString<200> newPath = StringRef(FE->getName());
fs::make_absolute(newPath);
infoOut << newPath << '\n';
} else {
SmallString<64> tempPath;
int fd;
if (fs::createTemporaryFile(
path::filename(origFE.getName()),
path::extension(origFE.getName()).drop_front(), fd, tempPath,
llvm::sys::fs::OF_Text))
return report("Could not create file: " + tempPath.str(), Diag);
llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true);
llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);
newOut.write(mem->getBufferStart(), mem->getBufferSize());
newOut.close();
auto newE = FileMgr->getOptionalFileRef(tempPath);
if (newE) {
remap(origFE, *newE);
infoOut << newE->getName() << '\n';
}
}
}
infoOut.close();
return false;
}
bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag,
StringRef outputDir) {
using namespace llvm::sys;
for (MappingsTy::iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
FileEntryRef origFE = I->first;
assert(std::holds_alternative<llvm::MemoryBuffer *>(I->second));
if (!fs::exists(origFE.getName()))
return report(StringRef("File does not exist: ") + origFE.getName(),
Diag);
std::error_code EC;
llvm::raw_fd_ostream Out(origFE.getName(), EC, llvm::sys::fs::OF_None);
if (EC)
return report(EC.message(), Diag);
llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);
Out.write(mem->getBufferStart(), mem->getBufferSize());
Out.close();
}
clear(outputDir);
return false;
}
void FileRemapper::forEachMapping(
llvm::function_ref<void(StringRef, StringRef)> CaptureFile,
llvm::function_ref<void(StringRef, const llvm::MemoryBufferRef &)>
CaptureBuffer) const {
for (auto &Mapping : FromToMappings) {
if (const auto *FE = std::get_if<FileEntryRef>(&Mapping.second)) {
CaptureFile(Mapping.first.getName(), FE->getName());
continue;
}
CaptureBuffer(
Mapping.first.getName(),
std::get<llvm::MemoryBuffer *>(Mapping.second)->getMemBufferRef());
}
}
void FileRemapper::applyMappings(PreprocessorOptions &PPOpts) const {
for (MappingsTy::const_iterator
I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) {
if (const auto *FE = std::get_if<FileEntryRef>(&I->second)) {
PPOpts.addRemappedFile(I->first.getName(), FE->getName());
} else {
llvm::MemoryBuffer *mem = std::get<llvm::MemoryBuffer *>(I->second);
PPOpts.addRemappedFile(I->first.getName(), mem);
}
}
PPOpts.RetainRemappedFileBuffers = true;
}
void FileRemapper::remap(StringRef filePath,
std::unique_ptr<llvm::MemoryBuffer> memBuf) {
OptionalFileEntryRef File = getOriginalFile(filePath);
assert(File);
remap(*File, std::move(memBuf));
}
void FileRemapper::remap(FileEntryRef File,
std::unique_ptr<llvm::MemoryBuffer> MemBuf) {
auto [It, New] = FromToMappings.insert({File, nullptr});
if (!New)
resetTarget(It->second);
It->second = MemBuf.release();
}
void FileRemapper::remap(FileEntryRef File, FileEntryRef NewFile) {
auto [It, New] = FromToMappings.insert({File, nullptr});
if (!New)
resetTarget(It->second);
It->second = NewFile;
ToFromMappings.insert({NewFile, File});
}
OptionalFileEntryRef FileRemapper::getOriginalFile(StringRef filePath) {
OptionalFileEntryRef File = FileMgr->getOptionalFileRef(filePath);
if (!File)
return std::nullopt;
// If we are updating a file that overridden an original file,
// actually update the original file.
auto I = ToFromMappings.find(*File);
if (I != ToFromMappings.end()) {
*File = I->second;
assert(FromToMappings.contains(*File) && "Original file not in mappings!");
}
return File;
}
void FileRemapper::resetTarget(Target &targ) {
if (std::holds_alternative<llvm::MemoryBuffer *>(targ)) {
llvm::MemoryBuffer *oldmem = std::get<llvm::MemoryBuffer *>(targ);
delete oldmem;
} else {
FileEntryRef toFE = std::get<FileEntryRef>(targ);
ToFromMappings.erase(toFE);
}
}
bool FileRemapper::report(const Twine &err, DiagnosticsEngine &Diag) {
Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
<< err.str();
return true;
}

@ -1,180 +0,0 @@
//===-- Internals.h - Implementation Details---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H
#define LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/MigratorOptions.h"
#include "llvm/ADT/ArrayRef.h"
#include <list>
#include <optional>
namespace clang {
class ASTContext;
class Sema;
class Stmt;
namespace arcmt {
class CapturedDiagList {
typedef std::list<StoredDiagnostic> ListTy;
ListTy List;
public:
void push_back(const StoredDiagnostic &diag) { List.push_back(diag); }
bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
bool hasDiagnostic(ArrayRef<unsigned> IDs, SourceRange range) const;
void reportDiagnostics(DiagnosticsEngine &diags) const;
bool hasErrors() const;
typedef ListTy::const_iterator iterator;
iterator begin() const { return List.begin(); }
iterator end() const { return List.end(); }
};
void writeARCDiagsToPlist(const std::string &outPath,
ArrayRef<StoredDiagnostic> diags,
SourceManager &SM, const LangOptions &LangOpts);
class TransformActions {
DiagnosticsEngine &Diags;
CapturedDiagList &CapturedDiags;
void *Impl; // TransformActionsImpl.
public:
TransformActions(DiagnosticsEngine &diag, CapturedDiagList &capturedDiags,
ASTContext &ctx, Preprocessor &PP);
~TransformActions();
void startTransaction();
bool commitTransaction();
void abortTransaction();
void insert(SourceLocation loc, StringRef text);
void insertAfterToken(SourceLocation loc, StringRef text);
void remove(SourceRange range);
void removeStmt(Stmt *S);
void replace(SourceRange range, StringRef text);
void replace(SourceRange range, SourceRange replacementRange);
void replaceStmt(Stmt *S, StringRef text);
void replaceText(SourceLocation loc, StringRef text,
StringRef replacementText);
void increaseIndentation(SourceRange range,
SourceLocation parentIndent);
bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
bool clearAllDiagnostics(SourceRange range) {
return clearDiagnostic({}, range);
}
bool clearDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) {
unsigned IDs[] = { ID1, ID2 };
return clearDiagnostic(IDs, range);
}
bool clearDiagnostic(unsigned ID1, unsigned ID2, unsigned ID3,
SourceRange range) {
unsigned IDs[] = { ID1, ID2, ID3 };
return clearDiagnostic(IDs, range);
}
bool hasDiagnostic(unsigned ID, SourceRange range) {
return CapturedDiags.hasDiagnostic(ID, range);
}
bool hasDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) {
unsigned IDs[] = { ID1, ID2 };
return CapturedDiags.hasDiagnostic(IDs, range);
}
DiagnosticBuilder report(SourceLocation loc, unsigned diagId,
SourceRange range = SourceRange());
void reportError(StringRef error, SourceLocation loc,
SourceRange range = SourceRange());
void reportWarning(StringRef warning, SourceLocation loc,
SourceRange range = SourceRange());
void reportNote(StringRef note, SourceLocation loc,
SourceRange range = SourceRange());
bool hasReportedErrors() const {
return Diags.hasUnrecoverableErrorOccurred();
}
class RewriteReceiver {
public:
virtual ~RewriteReceiver();
virtual void insert(SourceLocation loc, StringRef text) = 0;
virtual void remove(CharSourceRange range) = 0;
virtual void increaseIndentation(CharSourceRange range,
SourceLocation parentIndent) = 0;
};
void applyRewrites(RewriteReceiver &receiver);
};
class Transaction {
TransformActions &TA;
bool Aborted;
public:
Transaction(TransformActions &TA) : TA(TA), Aborted(false) {
TA.startTransaction();
}
~Transaction() {
if (!isAborted())
TA.commitTransaction();
}
void abort() {
TA.abortTransaction();
Aborted = true;
}
bool isAborted() const { return Aborted; }
};
class MigrationPass {
public:
ASTContext &Ctx;
LangOptions::GCMode OrigGCMode;
MigratorOptions MigOptions;
Sema &SemaRef;
TransformActions &TA;
const CapturedDiagList &CapturedDiags;
std::vector<SourceLocation> &ARCMTMacroLocs;
std::optional<bool> EnableCFBridgeFns;
MigrationPass(ASTContext &Ctx, LangOptions::GCMode OrigGCMode, Sema &sema,
TransformActions &TA, const CapturedDiagList &capturedDiags,
std::vector<SourceLocation> &ARCMTMacroLocs)
: Ctx(Ctx), OrigGCMode(OrigGCMode), SemaRef(sema), TA(TA),
CapturedDiags(capturedDiags), ARCMTMacroLocs(ARCMTMacroLocs) {}
const CapturedDiagList &getDiags() const { return CapturedDiags; }
bool isGCMigration() const { return OrigGCMode != LangOptions::NonGC; }
bool noFinalizeRemoval() const { return MigOptions.NoFinalizeRemoval; }
void setNoFinalizeRemoval(bool val) {MigOptions.NoFinalizeRemoval = val; }
bool CFBridgingFunctionsDefined();
};
static inline StringRef getARCMTMacroName() {
return "__IMPL_ARCMT_REMOVED_EXPR__";
}
} // end namespace arcmt
} // end namespace clang
#endif

File diff suppressed because it is too large Load Diff

@ -1,124 +0,0 @@
//===--- PlistReporter.cpp - ARC Migrate Tool Plist Reporter ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Internals.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/PlistSupport.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
using namespace arcmt;
using namespace markup;
static StringRef getLevelName(DiagnosticsEngine::Level Level) {
switch (Level) {
case DiagnosticsEngine::Ignored:
llvm_unreachable("ignored");
case DiagnosticsEngine::Note:
return "note";
case DiagnosticsEngine::Remark:
case DiagnosticsEngine::Warning:
return "warning";
case DiagnosticsEngine::Fatal:
case DiagnosticsEngine::Error:
return "error";
}
llvm_unreachable("Invalid DiagnosticsEngine level!");
}
void arcmt::writeARCDiagsToPlist(const std::string &outPath,
ArrayRef<StoredDiagnostic> diags,
SourceManager &SM,
const LangOptions &LangOpts) {
DiagnosticIDs DiagIDs;
// Build up a set of FIDs that we use by scanning the locations and
// ranges of the diagnostics.
FIDMap FM;
SmallVector<FileID, 10> Fids;
for (ArrayRef<StoredDiagnostic>::iterator
I = diags.begin(), E = diags.end(); I != E; ++I) {
const StoredDiagnostic &D = *I;
AddFID(FM, Fids, SM, D.getLocation());
for (StoredDiagnostic::range_iterator
RI = D.range_begin(), RE = D.range_end(); RI != RE; ++RI) {
AddFID(FM, Fids, SM, RI->getBegin());
AddFID(FM, Fids, SM, RI->getEnd());
}
}
std::error_code EC;
llvm::raw_fd_ostream o(outPath, EC, llvm::sys::fs::OF_TextWithCRLF);
if (EC) {
llvm::errs() << "error: could not create file: " << outPath << '\n';
return;
}
EmitPlistHeader(o);
// Write the root object: a <dict> containing...
// - "files", an <array> mapping from FIDs to file names
// - "diagnostics", an <array> containing the diagnostics
o << "<dict>\n"
" <key>files</key>\n"
" <array>\n";
for (FileID FID : Fids)
EmitString(o << " ", SM.getFileEntryRefForID(FID)->getName()) << '\n';
o << " </array>\n"
" <key>diagnostics</key>\n"
" <array>\n";
for (ArrayRef<StoredDiagnostic>::iterator
DI = diags.begin(), DE = diags.end(); DI != DE; ++DI) {
const StoredDiagnostic &D = *DI;
if (D.getLevel() == DiagnosticsEngine::Ignored)
continue;
o << " <dict>\n";
// Output the diagnostic.
o << " <key>description</key>";
EmitString(o, D.getMessage()) << '\n';
o << " <key>category</key>";
EmitString(o, DiagIDs.getCategoryNameFromID(
DiagIDs.getCategoryNumberForDiag(D.getID()))) << '\n';
o << " <key>type</key>";
EmitString(o, getLevelName(D.getLevel())) << '\n';
// Output the location of the bug.
o << " <key>location</key>\n";
EmitLocation(o, SM, D.getLocation(), FM, 2);
// Output the ranges (if any).
if (!D.getRanges().empty()) {
o << " <key>ranges</key>\n";
o << " <array>\n";
for (auto &R : D.getRanges()) {
CharSourceRange ExpansionRange = SM.getExpansionRange(R);
EmitRange(o, SM, Lexer::getAsCharRange(ExpansionRange, SM, LangOpts),
FM, 4);
}
o << " </array>\n";
}
// Close up the entry.
o << " </dict>\n";
}
o << " </array>\n";
// Finish.
o << "</dict>\n</plist>\n";
}

@ -1,107 +0,0 @@
//===--- TransAPIUses.cpp - Transformations to ARC mode -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// checkAPIUses:
//
// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
//
// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
// with __unsafe_unretained objects.
// - Calling -zone gets replaced with 'nil'.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class APIChecker : public RecursiveASTVisitor<APIChecker> {
MigrationPass &Pass;
Selector getReturnValueSel, setReturnValueSel;
Selector getArgumentSel, setArgumentSel;
Selector zoneSel;
public:
APIChecker(MigrationPass &pass) : Pass(pass) {
SelectorTable &sels = Pass.Ctx.Selectors;
IdentifierTable &ids = Pass.Ctx.Idents;
getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
const IdentifierInfo *selIds[2];
selIds[0] = &ids.get("getArgument");
selIds[1] = &ids.get("atIndex");
getArgumentSel = sels.getSelector(2, selIds);
selIds[0] = &ids.get("setArgument");
setArgumentSel = sels.getSelector(2, selIds);
zoneSel = sels.getNullarySelector(&ids.get("zone"));
}
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
// NSInvocation.
if (E->isInstanceMessage() &&
E->getReceiverInterface() &&
E->getReceiverInterface()->getName() == "NSInvocation") {
StringRef selName;
if (E->getSelector() == getReturnValueSel)
selName = "getReturnValue";
else if (E->getSelector() == setReturnValueSel)
selName = "setReturnValue";
else if (E->getSelector() == getArgumentSel)
selName = "getArgument";
else if (E->getSelector() == setArgumentSel)
selName = "setArgument";
else
return true;
Expr *parm = E->getArg(0)->IgnoreParenCasts();
QualType pointee = parm->getType()->getPointeeType();
if (pointee.isNull())
return true;
if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone)
Pass.TA.report(parm->getBeginLoc(),
diag::err_arcmt_nsinvocation_ownership,
parm->getSourceRange())
<< selName;
return true;
}
// -zone.
if (E->isInstanceMessage() &&
E->getInstanceReceiver() &&
E->getSelector() == zoneSel &&
Pass.TA.hasDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
E->getSelectorLoc(0))) {
// Calling -zone is meaningless in ARC, change it to nil.
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
E->getSelectorLoc(0));
Pass.TA.replace(E->getSourceRange(), getNilString(Pass));
}
return true;
}
};
} // anonymous namespace
void trans::checkAPIUses(MigrationPass &pass) {
APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,77 +0,0 @@
//===--- TransARCAssign.cpp - Transformations to ARC mode -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// makeAssignARCSafe:
//
// Add '__strong' where appropriate.
//
// for (id x in collection) {
// x = 0;
// }
// ---->
// for (__strong id x in collection) {
// x = 0;
// }
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class ARCAssignChecker : public RecursiveASTVisitor<ARCAssignChecker> {
MigrationPass &Pass;
llvm::DenseSet<VarDecl *> ModifiedVars;
public:
ARCAssignChecker(MigrationPass &pass) : Pass(pass) { }
bool VisitBinaryOperator(BinaryOperator *Exp) {
if (Exp->getType()->isDependentType())
return true;
Expr *E = Exp->getLHS();
SourceLocation OrigLoc = E->getExprLoc();
SourceLocation Loc = OrigLoc;
DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts());
if (declRef && isa<VarDecl>(declRef->getDecl())) {
ASTContext &Ctx = Pass.Ctx;
Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(Ctx, &Loc);
if (IsLV != Expr::MLV_ConstQualified)
return true;
VarDecl *var = cast<VarDecl>(declRef->getDecl());
if (var->isARCPseudoStrong()) {
Transaction Trans(Pass.TA);
if (Pass.TA.clearDiagnostic(diag::err_typecheck_arr_assign_enumeration,
Exp->getOperatorLoc())) {
if (!ModifiedVars.count(var)) {
TypeLoc TLoc = var->getTypeSourceInfo()->getTypeLoc();
Pass.TA.insert(TLoc.getBeginLoc(), "__strong ");
ModifiedVars.insert(var);
}
}
}
}
return true;
}
};
} // anonymous namespace
void trans::makeAssignARCSafe(MigrationPass &pass) {
ARCAssignChecker assignCheck(pass);
assignCheck.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,435 +0,0 @@
//===--- TransAutoreleasePool.cpp - Transformations to ARC mode -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// rewriteAutoreleasePool:
//
// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.
//
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ...
// [pool release];
// ---->
// @autorelease {
// ...
// }
//
// An NSAutoreleasePool will not be touched if:
// - There is not a corresponding -release/-drain in the same scope
// - Not all references of the NSAutoreleasePool variable can be removed
// - There is a variable that is declared inside the intended @autorelease scope
// which is also used outside it.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Sema/SemaDiagnostic.h"
#include <map>
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {
Decl *Dcl;
SmallVectorImpl<ObjCMessageExpr *> &Releases;
public:
ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)
: Dcl(D), Releases(releases) { }
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
if (!E->isInstanceMessage())
return true;
if (E->getMethodFamily() != OMF_release)
return true;
Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();
if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {
if (DE->getDecl() == Dcl)
Releases.push_back(E);
}
return true;
}
};
}
namespace {
class AutoreleasePoolRewriter
: public RecursiveASTVisitor<AutoreleasePoolRewriter> {
public:
AutoreleasePoolRewriter(MigrationPass &pass)
: Body(nullptr), Pass(pass) {
PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");
DrainSel = pass.Ctx.Selectors.getNullarySelector(
&pass.Ctx.Idents.get("drain"));
}
void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
TraverseStmt(body);
}
~AutoreleasePoolRewriter() {
SmallVector<VarDecl *, 8> VarsToHandle;
for (std::map<VarDecl *, PoolVarInfo>::iterator
I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {
VarDecl *var = I->first;
PoolVarInfo &info = I->second;
// Check that we can handle/rewrite all references of the pool.
clearRefsIn(info.Dcl, info.Refs);
for (SmallVectorImpl<PoolScope>::iterator
scpI = info.Scopes.begin(),
scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
PoolScope &scope = *scpI;
clearRefsIn(*scope.Begin, info.Refs);
clearRefsIn(*scope.End, info.Refs);
clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);
}
// Even if one reference is not handled we will not do anything about that
// pool variable.
if (info.Refs.empty())
VarsToHandle.push_back(var);
}
for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {
PoolVarInfo &info = PoolVars[VarsToHandle[i]];
Transaction Trans(Pass.TA);
clearUnavailableDiags(info.Dcl);
Pass.TA.removeStmt(info.Dcl);
// Add "@autoreleasepool { }"
for (SmallVectorImpl<PoolScope>::iterator
scpI = info.Scopes.begin(),
scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
PoolScope &scope = *scpI;
clearUnavailableDiags(*scope.Begin);
clearUnavailableDiags(*scope.End);
if (scope.IsFollowedBySimpleReturnStmt) {
// Include the return in the scope.
Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
Pass.TA.removeStmt(*scope.End);
Stmt::child_iterator retI = scope.End;
++retI;
SourceLocation afterSemi =
findLocationAfterSemi((*retI)->getEndLoc(), Pass.Ctx);
assert(afterSemi.isValid() &&
"Didn't we check before setting IsFollowedBySimpleReturnStmt "
"to true?");
Pass.TA.insertAfterToken(afterSemi, "\n}");
Pass.TA.increaseIndentation(
SourceRange(scope.getIndentedRange().getBegin(),
(*retI)->getEndLoc()),
scope.CompoundParent->getBeginLoc());
} else {
Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
Pass.TA.replaceStmt(*scope.End, "}");
Pass.TA.increaseIndentation(scope.getIndentedRange(),
scope.CompoundParent->getBeginLoc());
}
}
// Remove rest of pool var references.
for (SmallVectorImpl<PoolScope>::iterator
scpI = info.Scopes.begin(),
scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
PoolScope &scope = *scpI;
for (SmallVectorImpl<ObjCMessageExpr *>::iterator
relI = scope.Releases.begin(),
relE = scope.Releases.end(); relI != relE; ++relI) {
clearUnavailableDiags(*relI);
Pass.TA.removeStmt(*relI);
}
}
}
}
bool VisitCompoundStmt(CompoundStmt *S) {
SmallVector<PoolScope, 4> Scopes;
for (Stmt::child_iterator
I = S->body_begin(), E = S->body_end(); I != E; ++I) {
Stmt *child = getEssential(*I);
if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {
if (DclS->isSingleDecl()) {
if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {
if (isNSAutoreleasePool(VD->getType())) {
PoolVarInfo &info = PoolVars[VD];
info.Dcl = DclS;
collectRefs(VD, S, info.Refs);
// Does this statement follow the pattern:
// NSAutoreleasePool * pool = [NSAutoreleasePool new];
if (isPoolCreation(VD->getInit())) {
Scopes.push_back(PoolScope());
Scopes.back().PoolVar = VD;
Scopes.back().CompoundParent = S;
Scopes.back().Begin = I;
}
}
}
}
} else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {
if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {
if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {
// Does this statement follow the pattern:
// pool = [NSAutoreleasePool new];
if (isNSAutoreleasePool(VD->getType()) &&
isPoolCreation(bop->getRHS())) {
Scopes.push_back(PoolScope());
Scopes.back().PoolVar = VD;
Scopes.back().CompoundParent = S;
Scopes.back().Begin = I;
}
}
}
}
if (Scopes.empty())
continue;
if (isPoolDrain(Scopes.back().PoolVar, child)) {
PoolScope &scope = Scopes.back();
scope.End = I;
handlePoolScope(scope, S);
Scopes.pop_back();
}
}
return true;
}
private:
void clearUnavailableDiags(Stmt *S) {
if (S)
Pass.TA.clearDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
S->getSourceRange());
}
struct PoolScope {
VarDecl *PoolVar;
CompoundStmt *CompoundParent;
Stmt::child_iterator Begin;
Stmt::child_iterator End;
bool IsFollowedBySimpleReturnStmt;
SmallVector<ObjCMessageExpr *, 4> Releases;
PoolScope()
: PoolVar(nullptr), CompoundParent(nullptr),
IsFollowedBySimpleReturnStmt(false) {}
SourceRange getIndentedRange() const {
Stmt::child_iterator rangeS = Begin;
++rangeS;
if (rangeS == End)
return SourceRange();
Stmt::child_iterator rangeE = Begin;
for (Stmt::child_iterator I = rangeS; I != End; ++I)
++rangeE;
return SourceRange((*rangeS)->getBeginLoc(), (*rangeE)->getEndLoc());
}
};
class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
ASTContext &Ctx;
SourceRange ScopeRange;
SourceLocation &referenceLoc, &declarationLoc;
public:
NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
SourceLocation &referenceLoc,
SourceLocation &declarationLoc)
: Ctx(ctx), referenceLoc(referenceLoc),
declarationLoc(declarationLoc) {
ScopeRange = SourceRange((*scope.Begin)->getBeginLoc(),
(*scope.End)->getBeginLoc());
}
bool VisitDeclRefExpr(DeclRefExpr *E) {
return checkRef(E->getLocation(), E->getDecl()->getLocation());
}
bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
}
bool VisitTagTypeLoc(TagTypeLoc TL) {
return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
}
private:
bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
if (isInScope(declLoc)) {
referenceLoc = refLoc;
declarationLoc = declLoc;
return false;
}
return true;
}
bool isInScope(SourceLocation loc) {
if (loc.isInvalid())
return false;
SourceManager &SM = Ctx.getSourceManager();
if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
return false;
return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
}
};
void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
// Check that all names declared inside the scope are not used
// outside the scope.
{
bool nameUsedOutsideScope = false;
SourceLocation referenceLoc, declarationLoc;
Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
++SI;
// Check if the autoreleasepool scope is followed by a simple return
// statement, in which case we will include the return in the scope.
if (SI != SE)
if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
if ((retS->getRetValue() == nullptr ||
isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
findLocationAfterSemi(retS->getEndLoc(), Pass.Ctx).isValid()) {
scope.IsFollowedBySimpleReturnStmt = true;
++SI; // the return will be included in scope, don't check it.
}
for (; SI != SE; ++SI) {
nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
referenceLoc,
declarationLoc).TraverseStmt(*SI);
if (nameUsedOutsideScope)
break;
}
// If not all references were cleared it means some variables/typenames/etc
// declared inside the pool scope are used outside of it.
// We won't try to rewrite the pool.
if (nameUsedOutsideScope) {
Pass.TA.reportError("a name is referenced outside the "
"NSAutoreleasePool scope that it was declared in", referenceLoc);
Pass.TA.reportNote("name declared here", declarationLoc);
Pass.TA.reportNote("intended @autoreleasepool scope begins here",
(*scope.Begin)->getBeginLoc());
Pass.TA.reportNote("intended @autoreleasepool scope ends here",
(*scope.End)->getBeginLoc());
return;
}
}
// Collect all releases of the pool; they will be removed.
{
ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
Stmt::child_iterator I = scope.Begin;
++I;
for (; I != scope.End; ++I)
releaseColl.TraverseStmt(*I);
}
PoolVars[scope.PoolVar].Scopes.push_back(scope);
}
bool isPoolCreation(Expr *E) {
if (!E) return false;
E = getEssential(E);
ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
if (!ME) return false;
if (ME->getMethodFamily() == OMF_new &&
ME->getReceiverKind() == ObjCMessageExpr::Class &&
isNSAutoreleasePool(ME->getReceiverInterface()))
return true;
if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
ME->getMethodFamily() == OMF_init) {
Expr *rec = getEssential(ME->getInstanceReceiver());
if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
if (recME->getMethodFamily() == OMF_alloc &&
recME->getReceiverKind() == ObjCMessageExpr::Class &&
isNSAutoreleasePool(recME->getReceiverInterface()))
return true;
}
}
return false;
}
bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
if (!S) return false;
S = getEssential(S);
ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
if (!ME) return false;
if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
Expr *rec = getEssential(ME->getInstanceReceiver());
if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
if (dref->getDecl() == poolVar)
return ME->getMethodFamily() == OMF_release ||
ME->getSelector() == DrainSel;
}
return false;
}
bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
return IDecl && IDecl->getIdentifier() == PoolII;
}
bool isNSAutoreleasePool(QualType Ty) {
QualType pointee = Ty->getPointeeType();
if (pointee.isNull())
return false;
if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
return isNSAutoreleasePool(interT->getDecl());
return false;
}
static Expr *getEssential(Expr *E) {
return cast<Expr>(getEssential((Stmt*)E));
}
static Stmt *getEssential(Stmt *S) {
if (FullExpr *FE = dyn_cast<FullExpr>(S))
S = FE->getSubExpr();
if (Expr *E = dyn_cast<Expr>(S))
S = E->IgnoreParenCasts();
return S;
}
Stmt *Body;
MigrationPass &Pass;
IdentifierInfo *PoolII;
Selector DrainSel;
struct PoolVarInfo {
DeclStmt *Dcl = nullptr;
ExprSet Refs;
SmallVector<PoolScope, 2> Scopes;
PoolVarInfo() = default;
};
std::map<VarDecl *, PoolVarInfo> PoolVars;
};
} // anonymous namespace
void trans::rewriteAutoreleasePool(MigrationPass &pass) {
BodyTransform<AutoreleasePoolRewriter> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,146 +0,0 @@
//===--- TransBlockObjCVariable.cpp - Transformations to ARC mode ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// rewriteBlockObjCVariable:
//
// Adding __block to an obj-c variable could be either because the variable
// is used for output storage or the user wanted to break a retain cycle.
// This transformation checks whether a reference of the variable for the block
// is actually needed (it is assigned to or its address is taken) or not.
// If the reference is not needed it will assume __block was added to break a
// cycle so it will remove '__block' and add __weak/__unsafe_unretained.
// e.g
//
// __block Foo *x;
// bar(^ { [x cake]; });
// ---->
// __weak Foo *x;
// bar(^ { [x cake]; });
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/SourceManager.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class RootBlockObjCVarRewriter :
public RecursiveASTVisitor<RootBlockObjCVarRewriter> {
llvm::DenseSet<VarDecl *> &VarsToChange;
class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> {
VarDecl *Var;
typedef RecursiveASTVisitor<BlockVarChecker> base;
public:
BlockVarChecker(VarDecl *var) : Var(var) { }
bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) {
if (DeclRefExpr *
ref = dyn_cast<DeclRefExpr>(castE->getSubExpr())) {
if (ref->getDecl() == Var) {
if (castE->getCastKind() == CK_LValueToRValue)
return true; // Using the value of the variable.
if (castE->getCastKind() == CK_NoOp && castE->isLValue() &&
Var->getASTContext().getLangOpts().CPlusPlus)
return true; // Binding to const C++ reference.
}
}
return base::TraverseImplicitCastExpr(castE);
}
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (E->getDecl() == Var)
return false; // The reference of the variable, and not just its value,
// is needed.
return true;
}
};
public:
RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
: VarsToChange(VarsToChange) { }
bool VisitBlockDecl(BlockDecl *block) {
SmallVector<VarDecl *, 4> BlockVars;
for (const auto &I : block->captures()) {
VarDecl *var = I.getVariable();
if (I.isByRef() &&
var->getType()->isObjCObjectPointerType() &&
isImplicitStrong(var->getType())) {
BlockVars.push_back(var);
}
}
for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) {
VarDecl *var = BlockVars[i];
BlockVarChecker checker(var);
bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody());
if (onlyValueOfVarIsNeeded)
VarsToChange.insert(var);
else
VarsToChange.erase(var);
}
return true;
}
private:
bool isImplicitStrong(QualType ty) {
if (isa<AttributedType>(ty.getTypePtr()))
return false;
return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong;
}
};
class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> {
llvm::DenseSet<VarDecl *> &VarsToChange;
public:
BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
: VarsToChange(VarsToChange) { }
bool TraverseBlockDecl(BlockDecl *block) {
RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block);
return true;
}
};
} // anonymous namespace
void BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) {
MigrationPass &Pass = BodyCtx.getMigrationContext().Pass;
llvm::DenseSet<VarDecl *> VarsToChange;
BlockObjCVarRewriter trans(VarsToChange);
trans.TraverseStmt(BodyCtx.getTopStmt());
for (llvm::DenseSet<VarDecl *>::iterator
I = VarsToChange.begin(), E = VarsToChange.end(); I != E; ++I) {
VarDecl *var = *I;
BlocksAttr *attr = var->getAttr<BlocksAttr>();
if(!attr)
continue;
bool useWeak = canApplyWeak(Pass.Ctx, var->getType());
SourceManager &SM = Pass.Ctx.getSourceManager();
Transaction Trans(Pass.TA);
Pass.TA.replaceText(SM.getExpansionLoc(attr->getLocation()),
"__block",
useWeak ? "__weak" : "__unsafe_unretained");
}
}

@ -1,249 +0,0 @@
//===-- TransEmptyStatementsAndDealloc.cpp - Transformations to ARC mode --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// removeEmptyStatementsAndDealloc:
//
// Removes empty statements that are leftovers from previous transformations.
// e.g for
//
// [x retain];
//
// removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements
// will remove.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceManager.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
static bool isEmptyARCMTMacroStatement(NullStmt *S,
std::vector<SourceLocation> &MacroLocs,
ASTContext &Ctx) {
if (!S->hasLeadingEmptyMacro())
return false;
SourceLocation SemiLoc = S->getSemiLoc();
if (SemiLoc.isInvalid() || SemiLoc.isMacroID())
return false;
if (MacroLocs.empty())
return false;
SourceManager &SM = Ctx.getSourceManager();
std::vector<SourceLocation>::iterator I = llvm::upper_bound(
MacroLocs, SemiLoc, BeforeThanCompare<SourceLocation>(SM));
--I;
SourceLocation
AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
assert(AfterMacroLoc.isFileID());
if (AfterMacroLoc == SemiLoc)
return true;
SourceLocation::IntTy RelOffs = 0;
if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs))
return false;
if (RelOffs < 0)
return false;
// We make the reasonable assumption that a semicolon after 100 characters
// means that it is not the next token after our macro. If this assumption
// fails it is not critical, we will just fail to clear out, e.g., an empty
// 'if'.
if (RelOffs - getARCMTMacroName().size() > 100)
return false;
SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx);
return AfterMacroSemiLoc == SemiLoc;
}
namespace {
/// Returns true if the statement became empty due to previous
/// transformations.
class EmptyChecker : public StmtVisitor<EmptyChecker, bool> {
ASTContext &Ctx;
std::vector<SourceLocation> &MacroLocs;
public:
EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> &macroLocs)
: Ctx(ctx), MacroLocs(macroLocs) { }
bool VisitNullStmt(NullStmt *S) {
return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx);
}
bool VisitCompoundStmt(CompoundStmt *S) {
if (S->body_empty())
return false; // was already empty, not because of transformations.
for (auto *I : S->body())
if (!Visit(I))
return false;
return true;
}
bool VisitIfStmt(IfStmt *S) {
if (S->getConditionVariable())
return false;
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getThen() || !Visit(S->getThen()))
return false;
return !S->getElse() || Visit(S->getElse());
}
bool VisitWhileStmt(WhileStmt *S) {
if (S->getConditionVariable())
return false;
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitDoStmt(DoStmt *S) {
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
Expr *Exp = S->getCollection();
if (!Exp)
return false;
if (hasSideEffects(Exp, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
if (!S->getSubStmt())
return false;
return Visit(S->getSubStmt());
}
};
class EmptyStatementsRemover :
public RecursiveASTVisitor<EmptyStatementsRemover> {
MigrationPass &Pass;
public:
EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { }
bool TraverseStmtExpr(StmtExpr *E) {
CompoundStmt *S = E->getSubStmt();
for (CompoundStmt::body_iterator
I = S->body_begin(), E = S->body_end(); I != E; ++I) {
if (I != E - 1)
check(*I);
TraverseStmt(*I);
}
return true;
}
bool VisitCompoundStmt(CompoundStmt *S) {
for (auto *I : S->body())
check(I);
return true;
}
ASTContext &getContext() { return Pass.Ctx; }
private:
void check(Stmt *S) {
if (!S) return;
if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) {
Transaction Trans(Pass.TA);
Pass.TA.removeStmt(S);
}
}
};
} // anonymous namespace
static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx,
std::vector<SourceLocation> &MacroLocs) {
for (auto *I : body->body())
if (!EmptyChecker(Ctx, MacroLocs).Visit(I))
return false;
return true;
}
static void cleanupDeallocOrFinalize(MigrationPass &pass) {
ASTContext &Ctx = pass.Ctx;
TransformActions &TA = pass.TA;
DeclContext *DC = Ctx.getTranslationUnitDecl();
Selector FinalizeSel =
Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
impl_iterator;
for (impl_iterator I = impl_iterator(DC->decls_begin()),
E = impl_iterator(DC->decls_end()); I != E; ++I) {
ObjCMethodDecl *DeallocM = nullptr;
ObjCMethodDecl *FinalizeM = nullptr;
for (auto *MD : I->instance_methods()) {
if (!MD->hasBody())
continue;
if (MD->getMethodFamily() == OMF_dealloc) {
DeallocM = MD;
} else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
FinalizeM = MD;
}
}
if (DeallocM) {
if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
Transaction Trans(TA);
TA.remove(DeallocM->getSourceRange());
}
if (FinalizeM) {
Transaction Trans(TA);
TA.remove(FinalizeM->getSourceRange());
}
} else if (FinalizeM) {
if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
Transaction Trans(TA);
TA.remove(FinalizeM->getSourceRange());
} else {
Transaction Trans(TA);
TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc");
}
}
}
}
void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
cleanupDeallocOrFinalize(pass);
for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
Transaction Trans(pass.TA);
pass.TA.remove(pass.ARCMTMacroLocs[i]);
}
}

@ -1,350 +0,0 @@
//===--- TransGCAttrs.cpp - Transformations to ARC mode -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
/// Collects all the places where GC attributes __strong/__weak occur.
class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> {
MigrationContext &MigrateCtx;
bool FullyMigratable;
std::vector<ObjCPropertyDecl *> &AllProps;
typedef RecursiveASTVisitor<GCAttrsCollector> base;
public:
GCAttrsCollector(MigrationContext &ctx,
std::vector<ObjCPropertyDecl *> &AllProps)
: MigrateCtx(ctx), FullyMigratable(false),
AllProps(AllProps) { }
bool shouldWalkTypesOfTypeLocs() const { return false; }
bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
handleAttr(TL);
return true;
}
bool TraverseDecl(Decl *D) {
if (!D || D->isImplicit())
return true;
SaveAndRestore Save(FullyMigratable, isMigratable(D));
if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) {
lookForAttribute(PropD, PropD->getTypeSourceInfo());
AllProps.push_back(PropD);
} else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
lookForAttribute(DD, DD->getTypeSourceInfo());
}
return base::TraverseDecl(D);
}
void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) {
if (!TInfo)
return;
TypeLoc TL = TInfo->getTypeLoc();
while (TL) {
if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) {
TL = QL.getUnqualifiedLoc();
} else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) {
if (handleAttr(Attr, D))
break;
TL = Attr.getModifiedLoc();
} else if (MacroQualifiedTypeLoc MDTL =
TL.getAs<MacroQualifiedTypeLoc>()) {
TL = MDTL.getInnerLoc();
} else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) {
TL = Arr.getElementLoc();
} else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) {
TL = PT.getPointeeLoc();
} else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>())
TL = RT.getPointeeLoc();
else
break;
}
}
bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) {
auto *OwnershipAttr = TL.getAttrAs<ObjCOwnershipAttr>();
if (!OwnershipAttr)
return false;
SourceLocation Loc = OwnershipAttr->getLocation();
SourceLocation OrigLoc = Loc;
if (MigrateCtx.AttrSet.count(OrigLoc))
return true;
ASTContext &Ctx = MigrateCtx.Pass.Ctx;
SourceManager &SM = Ctx.getSourceManager();
if (Loc.isMacroID())
Loc = SM.getImmediateExpansionRange(Loc).getBegin();
StringRef Spell = OwnershipAttr->getKind()->getName();
MigrationContext::GCAttrOccurrence::AttrKind Kind;
if (Spell == "strong")
Kind = MigrationContext::GCAttrOccurrence::Strong;
else if (Spell == "weak")
Kind = MigrationContext::GCAttrOccurrence::Weak;
else
return false;
MigrateCtx.AttrSet.insert(OrigLoc);
MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence());
MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back();
Attr.Kind = Kind;
Attr.Loc = Loc;
Attr.ModifiedType = TL.getModifiedLoc().getType();
Attr.Dcl = D;
Attr.FullyMigratable = FullyMigratable;
return true;
}
bool isMigratable(Decl *D) {
if (isa<TranslationUnitDecl>(D))
return false;
if (isInMainFile(D))
return true;
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
return FD->hasBody();
if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D))
return hasObjCImpl(ContD);
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
for (const auto *MI : RD->methods()) {
if (MI->isOutOfLine())
return true;
}
return false;
}
return isMigratable(cast<Decl>(D->getDeclContext()));
}
static bool hasObjCImpl(Decl *D) {
if (!D)
return false;
if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
return ID->getImplementation() != nullptr;
if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
return CD->getImplementation() != nullptr;
return isa<ObjCImplDecl>(ContD);
}
return false;
}
bool isInMainFile(Decl *D) {
if (!D)
return false;
for (auto *I : D->redecls())
if (!isInMainFile(I->getLocation()))
return false;
return true;
}
bool isInMainFile(SourceLocation Loc) {
if (Loc.isInvalid())
return false;
SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
}
};
} // anonymous namespace
static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
TransformActions &TA = MigrateCtx.Pass.TA;
for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
if (Attr.FullyMigratable && Attr.Dcl) {
if (Attr.ModifiedType.isNull())
continue;
if (!Attr.ModifiedType->isObjCRetainableType()) {
TA.reportError("GC managed memory will become unmanaged in ARC",
Attr.Loc);
}
}
}
}
static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
TransformActions &TA = MigrateCtx.Pass.TA;
for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
if (Attr.ModifiedType.isNull() ||
!Attr.ModifiedType->isObjCRetainableType())
continue;
if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
/*AllowOnUnknownClass=*/true)) {
Transaction Trans(TA);
if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc))
TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
diag::err_arc_unsupported_weak_class,
Attr.Loc);
}
}
}
}
typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
static void checkAllAtProps(MigrationContext &MigrateCtx,
SourceLocation AtLoc,
IndivPropsTy &IndProps) {
if (IndProps.empty())
return;
for (IndivPropsTy::iterator
PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
QualType T = (*PI)->getType();
if (T.isNull() || !T->isObjCRetainableType())
return;
}
SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
bool hasWeak = false, hasStrong = false;
ObjCPropertyAttribute::Kind Attrs = ObjCPropertyAttribute::kind_noattr;
for (IndivPropsTy::iterator
PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
ObjCPropertyDecl *PD = *PI;
Attrs = PD->getPropertyAttributesAsWritten();
TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
if (!TInfo)
return;
TypeLoc TL = TInfo->getTypeLoc();
if (AttributedTypeLoc ATL =
TL.getAs<AttributedTypeLoc>()) {
ATLs.push_back(std::make_pair(ATL, PD));
if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
hasWeak = true;
} else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
hasStrong = true;
else
return;
}
}
if (ATLs.empty())
return;
if (hasWeak && hasStrong)
return;
TransformActions &TA = MigrateCtx.Pass.TA;
Transaction Trans(TA);
if (GCAttrsCollector::hasObjCImpl(
cast<Decl>(IndProps.front()->getDeclContext()))) {
if (hasWeak)
MigrateCtx.AtPropsWeak.insert(AtLoc);
} else {
StringRef toAttr = "strong";
if (hasWeak) {
if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
/*AllowOnUnknownClass=*/true))
toAttr = "weak";
else
toAttr = "unsafe_unretained";
}
if (Attrs & ObjCPropertyAttribute::kind_assign)
MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
else
MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
}
for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
SourceLocation Loc = ATLs[i].first.getAttr()->getLocation();
if (Loc.isMacroID())
Loc = MigrateCtx.Pass.Ctx.getSourceManager()
.getImmediateExpansionRange(Loc)
.getBegin();
TA.remove(Loc);
TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
ATLs[i].second->getLocation());
MigrateCtx.RemovedAttrSet.insert(Loc);
}
}
static void checkAllProps(MigrationContext &MigrateCtx,
std::vector<ObjCPropertyDecl *> &AllProps) {
typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
llvm::DenseMap<SourceLocation, IndivPropsTy> AtProps;
for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
ObjCPropertyDecl *PD = AllProps[i];
if (PD->getPropertyAttributesAsWritten() &
(ObjCPropertyAttribute::kind_assign |
ObjCPropertyAttribute::kind_readonly)) {
SourceLocation AtLoc = PD->getAtLoc();
if (AtLoc.isInvalid())
continue;
AtProps[AtLoc].push_back(PD);
}
}
for (auto I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
SourceLocation AtLoc = I->first;
IndivPropsTy &IndProps = I->second;
checkAllAtProps(MigrateCtx, AtLoc, IndProps);
}
}
void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
std::vector<ObjCPropertyDecl *> AllProps;
GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
errorForGCAttrsOnNonObjC(MigrateCtx);
checkAllProps(MigrateCtx, AllProps);
checkWeakGCAttrs(MigrateCtx);
}
void MigrationContext::dumpGCAttrs() {
llvm::errs() << "\n################\n";
for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
GCAttrOccurrence &Attr = GCAttrs[i];
llvm::errs() << "KIND: "
<< (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
llvm::errs() << "\nLOC: ";
Attr.Loc.print(llvm::errs(), Pass.Ctx.getSourceManager());
llvm::errs() << "\nTYPE: ";
Attr.ModifiedType.dump();
if (Attr.Dcl) {
llvm::errs() << "DECL:\n";
Attr.Dcl->dump();
} else {
llvm::errs() << "DECL: NONE";
}
llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
llvm::errs() << "\n----------------\n";
}
llvm::errs() << "\n################\n";
}

@ -1,76 +0,0 @@
//===--- TransGCCalls.cpp - Transformations to ARC mode -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class GCCollectableCallsChecker :
public RecursiveASTVisitor<GCCollectableCallsChecker> {
MigrationContext &MigrateCtx;
IdentifierInfo *NSMakeCollectableII;
IdentifierInfo *CFMakeCollectableII;
public:
GCCollectableCallsChecker(MigrationContext &ctx)
: MigrateCtx(ctx) {
IdentifierTable &Ids = MigrateCtx.Pass.Ctx.Idents;
NSMakeCollectableII = &Ids.get("NSMakeCollectable");
CFMakeCollectableII = &Ids.get("CFMakeCollectable");
}
bool shouldWalkTypesOfTypeLocs() const { return false; }
bool VisitCallExpr(CallExpr *E) {
TransformActions &TA = MigrateCtx.Pass.TA;
if (MigrateCtx.isGCOwnedNonObjC(E->getType())) {
TA.report(E->getBeginLoc(), diag::warn_arcmt_nsalloc_realloc,
E->getSourceRange());
return true;
}
Expr *CEE = E->getCallee()->IgnoreParenImpCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) {
if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(DRE->getDecl())) {
if (!FD->getDeclContext()->getRedeclContext()->isFileContext())
return true;
if (FD->getIdentifier() == NSMakeCollectableII) {
Transaction Trans(TA);
TA.clearDiagnostic(diag::err_unavailable,
diag::err_unavailable_message,
diag::err_ovl_deleted_call, // ObjC++
DRE->getSourceRange());
TA.replace(DRE->getSourceRange(), "CFBridgingRelease");
} else if (FD->getIdentifier() == CFMakeCollectableII) {
TA.reportError("CFMakeCollectable will leak the object that it "
"receives in ARC", DRE->getLocation(),
DRE->getSourceRange());
}
}
}
return true;
}
};
} // anonymous namespace
void GCCollectableCallsTraverser::traverseBody(BodyContext &BodyCtx) {
GCCollectableCallsChecker(BodyCtx.getMigrationContext())
.TraverseStmt(BodyCtx.getTopStmt());
}

@ -1,379 +0,0 @@
//===--- TransProperties.cpp - Transformations to ARC mode ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// rewriteProperties:
//
// - Adds strong/weak/unsafe_unretained ownership specifier to properties that
// are missing one.
// - Migrates properties from (retain) to (strong) and (assign) to
// (unsafe_unretained/weak).
// - If a property is synthesized, adds the ownership specifier in the ivar
// backing the property.
//
// @interface Foo : NSObject {
// NSObject *x;
// }
// @property (assign) id x;
// @end
// ---->
// @interface Foo : NSObject {
// NSObject *__weak x;
// }
// @property (weak) id x;
// @end
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include <map>
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class PropertiesRewriter {
MigrationContext &MigrateCtx;
MigrationPass &Pass;
ObjCImplementationDecl *CurImplD = nullptr;
enum PropActionKind {
PropAction_None,
PropAction_RetainReplacedWithStrong,
PropAction_AssignRemoved,
PropAction_AssignRewritten,
PropAction_MaybeAddWeakOrUnsafe
};
struct PropData {
ObjCPropertyDecl *PropD;
ObjCIvarDecl *IvarD;
ObjCPropertyImplDecl *ImplD;
PropData(ObjCPropertyDecl *propD)
: PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
};
typedef SmallVector<PropData, 2> PropsTy;
typedef std::map<SourceLocation, PropsTy> AtPropDeclsTy;
AtPropDeclsTy AtProps;
llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
public:
explicit PropertiesRewriter(MigrationContext &MigrateCtx)
: MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
AtPropDeclsTy *PrevAtProps = nullptr) {
for (auto *Prop : D->instance_properties()) {
SourceLocation Loc = Prop->getAtLoc();
if (Loc.isInvalid())
continue;
if (PrevAtProps)
if (PrevAtProps->find(Loc) != PrevAtProps->end())
continue;
PropsTy &props = AtProps[Loc];
props.push_back(Prop);
}
}
void doTransform(ObjCImplementationDecl *D) {
CurImplD = D;
ObjCInterfaceDecl *iface = D->getClassInterface();
if (!iface)
return;
collectProperties(iface, AtProps);
// Look through extensions.
for (auto *Ext : iface->visible_extensions())
collectProperties(Ext, AtProps);
typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
prop_impl_iterator;
for (prop_impl_iterator
I = prop_impl_iterator(D->decls_begin()),
E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
ObjCPropertyImplDecl *implD = *I;
if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
continue;
ObjCPropertyDecl *propD = implD->getPropertyDecl();
if (!propD || propD->isInvalidDecl())
continue;
ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
if (!ivarD || ivarD->isInvalidDecl())
continue;
AtPropDeclsTy::iterator findAtLoc = AtProps.find(propD->getAtLoc());
if (findAtLoc == AtProps.end())
continue;
PropsTy &props = findAtLoc->second;
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
if (I->PropD == propD) {
I->IvarD = ivarD;
I->ImplD = implD;
break;
}
}
}
for (AtPropDeclsTy::iterator
I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
SourceLocation atLoc = I->first;
PropsTy &props = I->second;
if (!getPropertyType(props)->isObjCRetainableType())
continue;
if (hasIvarWithExplicitARCOwnership(props))
continue;
Transaction Trans(Pass.TA);
rewriteProperty(props, atLoc);
}
}
private:
void doPropAction(PropActionKind kind,
PropsTy &props, SourceLocation atLoc,
bool markAction = true) {
if (markAction)
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
ActionOnProp[I->PropD->getIdentifier()] = kind;
switch (kind) {
case PropAction_None:
return;
case PropAction_RetainReplacedWithStrong: {
StringRef toAttr = "strong";
MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
return;
}
case PropAction_AssignRemoved:
return removeAssignForDefaultStrong(props, atLoc);
case PropAction_AssignRewritten:
return rewriteAssign(props, atLoc);
case PropAction_MaybeAddWeakOrUnsafe:
return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
}
}
void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
ObjCPropertyAttribute::Kind propAttrs = getPropertyAttrs(props);
if (propAttrs &
(ObjCPropertyAttribute::kind_copy |
ObjCPropertyAttribute::kind_unsafe_unretained |
ObjCPropertyAttribute::kind_strong | ObjCPropertyAttribute::kind_weak))
return;
if (propAttrs & ObjCPropertyAttribute::kind_retain) {
// strong is the default.
return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
}
bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
if (propAttrs & ObjCPropertyAttribute::kind_assign) {
if (HasIvarAssignedAPlusOneObject)
return doPropAction(PropAction_AssignRemoved, props, atLoc);
return doPropAction(PropAction_AssignRewritten, props, atLoc);
}
if (HasIvarAssignedAPlusOneObject ||
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
return; // 'strong' by default.
return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
}
void removeAssignForDefaultStrong(PropsTy &props,
SourceLocation atLoc) const {
removeAttribute("retain", atLoc);
if (!removeAttribute("assign", atLoc))
return;
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
if (I->ImplD)
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
diag::err_arc_assign_property_ownership,
diag::err_arc_inconsistent_property_ownership,
I->IvarD->getLocation());
}
}
void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
/*AllowOnUnknownClass=*/Pass.isGCMigration());
const char *toWhich =
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
(canUseWeak ? "weak" : "unsafe_unretained");
bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
if (!rewroteAttr)
canUseWeak = false;
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
if (isUserDeclared(I->IvarD)) {
if (I->IvarD &&
I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
const char *toWhich =
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
(canUseWeak ? "__weak " : "__unsafe_unretained ");
Pass.TA.insert(I->IvarD->getLocation(), toWhich);
}
}
if (I->ImplD)
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
diag::err_arc_assign_property_ownership,
diag::err_arc_inconsistent_property_ownership,
I->IvarD->getLocation());
}
}
void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
SourceLocation atLoc) const {
bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
/*AllowOnUnknownClass=*/Pass.isGCMigration());
bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
atLoc);
if (!addedAttr)
canUseWeak = false;
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
if (isUserDeclared(I->IvarD)) {
if (I->IvarD &&
I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
Pass.TA.insert(I->IvarD->getLocation(),
canUseWeak ? "__weak " : "__unsafe_unretained ");
}
if (I->ImplD) {
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
diag::err_arc_assign_property_ownership,
diag::err_arc_inconsistent_property_ownership,
I->IvarD->getLocation());
Pass.TA.clearDiagnostic(
diag::err_arc_objc_property_default_assign_on_object,
I->ImplD->getLocation());
}
}
}
bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
}
bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
SourceLocation atLoc) const {
return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
}
bool addAttribute(StringRef attr, SourceLocation atLoc) const {
return MigrateCtx.addPropertyAttribute(attr, atLoc);
}
class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
ObjCIvarDecl *Ivar;
public:
PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
bool VisitBinaryOperator(BinaryOperator *E) {
if (E->getOpcode() != BO_Assign)
return true;
Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
if (RE->getDecl() != Ivar)
return true;
if (isPlusOneAssign(E))
return false;
}
return true;
}
};
bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
PlusOneAssign oneAssign(I->IvarD);
bool notFound = oneAssign.TraverseDecl(CurImplD);
if (!notFound)
return true;
}
return false;
}
bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
if (Pass.isGCMigration())
return false;
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
if (isUserDeclared(I->IvarD)) {
if (isa<AttributedType>(I->IvarD->getType()))
return true;
if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
!= Qualifiers::OCL_Strong)
return true;
}
}
return false;
}
// Returns true if all declarations in the @property have GC __weak.
bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
if (!Pass.isGCMigration())
return false;
if (props.empty())
return false;
return MigrateCtx.AtPropsWeak.count(atLoc);
}
bool isUserDeclared(ObjCIvarDecl *ivarD) const {
return ivarD && !ivarD->getSynthesize();
}
QualType getPropertyType(PropsTy &props) const {
assert(!props.empty());
QualType ty = props[0].PropD->getType().getUnqualifiedType();
#ifndef NDEBUG
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
assert(ty == I->PropD->getType().getUnqualifiedType());
#endif
return ty;
}
ObjCPropertyAttribute::Kind getPropertyAttrs(PropsTy &props) const {
assert(!props.empty());
ObjCPropertyAttribute::Kind attrs =
props[0].PropD->getPropertyAttributesAsWritten();
#ifndef NDEBUG
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
assert(attrs == I->PropD->getPropertyAttributesAsWritten());
#endif
return attrs;
}
};
} // anonymous namespace
void PropertyRewriteTraverser::traverseObjCImplementation(
ObjCImplementationContext &ImplCtx) {
PropertiesRewriter(ImplCtx.getMigrationContext())
.doTransform(ImplCtx.getImplementationDecl());
}

@ -1,203 +0,0 @@
//===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Adds brackets in case statements that "contain" initialization of retaining
// variable, thus emitting the "switch case is in protected scope" error.
//
//===----------------------------------------------------------------------===//
#include "Internals.h"
#include "Transforms.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
SmallVectorImpl<DeclRefExpr *> &Refs;
public:
LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
: Refs(refs) { }
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (ValueDecl *D = E->getDecl())
if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
Refs.push_back(E);
return true;
}
};
struct CaseInfo {
SwitchCase *SC;
SourceRange Range;
enum {
St_Unchecked,
St_CannotFix,
St_Fixed
} State;
CaseInfo() : SC(nullptr), State(St_Unchecked) {}
CaseInfo(SwitchCase *S, SourceRange Range)
: SC(S), Range(Range), State(St_Unchecked) {}
};
class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
ParentMap &PMap;
SmallVectorImpl<CaseInfo> &Cases;
public:
CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
: PMap(PMap), Cases(Cases) { }
bool VisitSwitchStmt(SwitchStmt *S) {
SwitchCase *Curr = S->getSwitchCaseList();
if (!Curr)
return true;
Stmt *Parent = getCaseParent(Curr);
Curr = Curr->getNextSwitchCase();
// Make sure all case statements are in the same scope.
while (Curr) {
if (getCaseParent(Curr) != Parent)
return true;
Curr = Curr->getNextSwitchCase();
}
SourceLocation NextLoc = S->getEndLoc();
Curr = S->getSwitchCaseList();
// We iterate over case statements in reverse source-order.
while (Curr) {
Cases.push_back(
CaseInfo(Curr, SourceRange(Curr->getBeginLoc(), NextLoc)));
NextLoc = Curr->getBeginLoc();
Curr = Curr->getNextSwitchCase();
}
return true;
}
Stmt *getCaseParent(SwitchCase *S) {
Stmt *Parent = PMap.getParent(S);
while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
Parent = PMap.getParent(Parent);
return Parent;
}
};
class ProtectedScopeFixer {
MigrationPass &Pass;
SourceManager &SM;
SmallVector<CaseInfo, 16> Cases;
SmallVector<DeclRefExpr *, 16> LocalRefs;
public:
ProtectedScopeFixer(BodyContext &BodyCtx)
: Pass(BodyCtx.getMigrationContext().Pass),
SM(Pass.Ctx.getSourceManager()) {
CaseCollector(BodyCtx.getParentMap(), Cases)
.TraverseStmt(BodyCtx.getTopStmt());
LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());
SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
const CapturedDiagList &DiagList = Pass.getDiags();
// Copy the diagnostics so we don't have to worry about invaliding iterators
// from the diagnostic list.
SmallVector<StoredDiagnostic, 16> StoredDiags;
StoredDiags.append(DiagList.begin(), DiagList.end());
SmallVectorImpl<StoredDiagnostic>::iterator
I = StoredDiags.begin(), E = StoredDiags.end();
while (I != E) {
if (I->getID() == diag::err_switch_into_protected_scope &&
isInRange(I->getLocation(), BodyRange)) {
handleProtectedScopeError(I, E);
continue;
}
++I;
}
}
void handleProtectedScopeError(
SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
Transaction Trans(Pass.TA);
assert(DiagI->getID() == diag::err_switch_into_protected_scope);
SourceLocation ErrLoc = DiagI->getLocation();
bool handledAllNotes = true;
++DiagI;
for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
++DiagI) {
if (!handleProtectedNote(*DiagI))
handledAllNotes = false;
}
if (handledAllNotes)
Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
}
bool handleProtectedNote(const StoredDiagnostic &Diag) {
assert(Diag.getLevel() == DiagnosticsEngine::Note);
for (unsigned i = 0; i != Cases.size(); i++) {
CaseInfo &info = Cases[i];
if (isInRange(Diag.getLocation(), info.Range)) {
if (info.State == CaseInfo::St_Unchecked)
tryFixing(info);
assert(info.State != CaseInfo::St_Unchecked);
if (info.State == CaseInfo::St_Fixed) {
Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
return true;
}
return false;
}
}
return false;
}
void tryFixing(CaseInfo &info) {
assert(info.State == CaseInfo::St_Unchecked);
if (hasVarReferencedOutside(info)) {
info.State = CaseInfo::St_CannotFix;
return;
}
Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
Pass.TA.insert(info.Range.getEnd(), "}\n");
info.State = CaseInfo::St_Fixed;
}
bool hasVarReferencedOutside(CaseInfo &info) {
for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
DeclRefExpr *DRE = LocalRefs[i];
if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
!isInRange(DRE->getLocation(), info.Range))
return true;
}
return false;
}
bool isInRange(SourceLocation Loc, SourceRange R) {
if (Loc.isInvalid())
return false;
return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
SM.isBeforeInTranslationUnit(Loc, R.getEnd());
}
};
} // anonymous namespace
void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
ProtectedScopeFixer Fix(BodyCtx);
}

@ -1,459 +0,0 @@
//===--- TransRetainReleaseDealloc.cpp - Transformations to ARC mode ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// removeRetainReleaseDealloc:
//
// Removes retain/release/autorelease/dealloc messages.
//
// return [[foo retain] autorelease];
// ---->
// return foo;
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class RetainReleaseDeallocRemover :
public RecursiveASTVisitor<RetainReleaseDeallocRemover> {
Stmt *Body;
MigrationPass &Pass;
ExprSet Removables;
std::unique_ptr<ParentMap> StmtMap;
Selector DelegateSel, FinalizeSel;
public:
RetainReleaseDeallocRemover(MigrationPass &pass)
: Body(nullptr), Pass(pass) {
DelegateSel =
Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
FinalizeSel =
Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
}
void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
collectRemovables(body, Removables);
StmtMap.reset(new ParentMap(body));
TraverseStmt(body);
}
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
switch (E->getMethodFamily()) {
default:
if (E->isInstanceMessage() && E->getSelector() == FinalizeSel)
break;
return true;
case OMF_autorelease:
if (isRemovable(E)) {
if (!isCommonUnusedAutorelease(E)) {
// An unused autorelease is badness. If we remove it the receiver
// will likely die immediately while previously it was kept alive
// by the autorelease pool. This is bad practice in general, leave it
// and emit an error to force the user to restructure their code.
Pass.TA.reportError(
"it is not safe to remove an unused 'autorelease' "
"message; its receiver may be destroyed immediately",
E->getBeginLoc(), E->getSourceRange());
return true;
}
}
// Pass through.
[[fallthrough]];
case OMF_retain:
case OMF_release:
if (E->getReceiverKind() == ObjCMessageExpr::Instance)
if (Expr *rec = E->getInstanceReceiver()) {
rec = rec->IgnoreParenImpCasts();
if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&
(E->getMethodFamily() != OMF_retain || isRemovable(E))) {
std::string err = "it is not safe to remove '";
err += E->getSelector().getAsString() + "' message on "
"an __unsafe_unretained type";
Pass.TA.reportError(err, rec->getBeginLoc());
return true;
}
if (isGlobalVar(rec) &&
(E->getMethodFamily() != OMF_retain || isRemovable(E))) {
std::string err = "it is not safe to remove '";
err += E->getSelector().getAsString() + "' message on "
"a global variable";
Pass.TA.reportError(err, rec->getBeginLoc());
return true;
}
if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {
Pass.TA.reportError(
"it is not safe to remove 'retain' "
"message on the result of a 'delegate' message; "
"the object that was passed to 'setDelegate:' may not be "
"properly retained",
rec->getBeginLoc());
return true;
}
}
break;
case OMF_dealloc:
break;
}
switch (E->getReceiverKind()) {
default:
return true;
case ObjCMessageExpr::SuperInstance: {
Transaction Trans(Pass.TA);
clearDiagnostics(E->getSelectorLoc(0));
if (tryRemoving(E))
return true;
Pass.TA.replace(E->getSourceRange(), "self");
return true;
}
case ObjCMessageExpr::Instance:
break;
}
Expr *rec = E->getInstanceReceiver();
if (!rec) return true;
Transaction Trans(Pass.TA);
clearDiagnostics(E->getSelectorLoc(0));
ObjCMessageExpr *Msg = E;
Expr *RecContainer = Msg;
SourceRange RecRange = rec->getSourceRange();
checkForGCDOrXPC(Msg, RecContainer, rec, RecRange);
if (Msg->getMethodFamily() == OMF_release &&
isRemovable(RecContainer) && isInAtFinally(RecContainer)) {
// Change the -release to "receiver = nil" in a finally to avoid a leak
// when an exception is thrown.
Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
std::string str = " = ";
str += getNilString(Pass);
Pass.TA.insertAfterToken(RecRange.getEnd(), str);
return true;
}
if (hasSideEffects(rec, Pass.Ctx) || !tryRemoving(RecContainer))
Pass.TA.replace(RecContainer->getSourceRange(), RecRange);
return true;
}
private:
/// Checks for idioms where an unused -autorelease is common.
///
/// Returns true for this idiom which is common in property
/// setters:
///
/// [backingValue autorelease];
/// backingValue = [newValue retain]; // in general a +1 assign
///
/// For these as well:
///
/// [[var retain] autorelease];
/// return var;
///
bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
return isPlusOneAssignBeforeOrAfterAutorelease(E) ||
isReturnedAfterAutorelease(E);
}
bool isReturnedAfterAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
Decl *RefD = getReferencedDecl(Rec);
if (!RefD)
return false;
Stmt *nextStmt = getNextStmt(E);
if (!nextStmt)
return false;
// Check for "return <variable>;".
if (ReturnStmt *RetS = dyn_cast<ReturnStmt>(nextStmt))
return RefD == getReferencedDecl(RetS->getRetValue());
return false;
}
bool isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
Decl *RefD = getReferencedDecl(Rec);
if (!RefD)
return false;
Stmt *prevStmt, *nextStmt;
std::tie(prevStmt, nextStmt) = getPreviousAndNextStmt(E);
return isPlusOneAssignToVar(prevStmt, RefD) ||
isPlusOneAssignToVar(nextStmt, RefD);
}
bool isPlusOneAssignToVar(Stmt *S, Decl *RefD) {
if (!S)
return false;
// Check for "RefD = [+1 retained object];".
if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) {
return (RefD == getReferencedDecl(Bop->getLHS())) && isPlusOneAssign(Bop);
}
if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
if (DS->isSingleDecl() && DS->getSingleDecl() == RefD) {
if (VarDecl *VD = dyn_cast<VarDecl>(RefD))
return isPlusOne(VD->getInit());
}
return false;
}
return false;
}
Stmt *getNextStmt(Expr *E) {
return getPreviousAndNextStmt(E).second;
}
std::pair<Stmt *, Stmt *> getPreviousAndNextStmt(Expr *E) {
Stmt *prevStmt = nullptr, *nextStmt = nullptr;
if (!E)
return std::make_pair(prevStmt, nextStmt);
Stmt *OuterS = E, *InnerS;
do {
InnerS = OuterS;
OuterS = StmtMap->getParent(InnerS);
}
while (OuterS && (isa<ParenExpr>(OuterS) ||
isa<CastExpr>(OuterS) ||
isa<FullExpr>(OuterS)));
if (!OuterS)
return std::make_pair(prevStmt, nextStmt);
Stmt::child_iterator currChildS = OuterS->child_begin();
Stmt::child_iterator childE = OuterS->child_end();
Stmt::child_iterator prevChildS = childE;
for (; currChildS != childE; ++currChildS) {
if (*currChildS == InnerS)
break;
prevChildS = currChildS;
}
if (prevChildS != childE) {
prevStmt = *prevChildS;
if (auto *E = dyn_cast_or_null<Expr>(prevStmt))
prevStmt = E->IgnoreImplicit();
}
if (currChildS == childE)
return std::make_pair(prevStmt, nextStmt);
++currChildS;
if (currChildS == childE)
return std::make_pair(prevStmt, nextStmt);
nextStmt = *currChildS;
if (auto *E = dyn_cast_or_null<Expr>(nextStmt))
nextStmt = E->IgnoreImplicit();
return std::make_pair(prevStmt, nextStmt);
}
Decl *getReferencedDecl(Expr *E) {
if (!E)
return nullptr;
E = E->IgnoreParenCasts();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
switch (ME->getMethodFamily()) {
case OMF_copy:
case OMF_autorelease:
case OMF_release:
case OMF_retain:
return getReferencedDecl(ME->getInstanceReceiver());
default:
return nullptr;
}
}
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl();
if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
return ME->getMemberDecl();
if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))
return IRE->getDecl();
return nullptr;
}
/// Check if the retain/release is due to a GCD/XPC macro that are
/// defined as:
///
/// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
/// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
/// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
/// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
///
/// and return the top container which is the StmtExpr and the macro argument
/// expression.
void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer,
Expr *&Rec, SourceRange &RecRange) {
SourceLocation Loc = Msg->getExprLoc();
if (!Loc.isMacroID())
return;
SourceManager &SM = Pass.Ctx.getSourceManager();
StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM,
Pass.Ctx.getLangOpts());
bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName)
.Case("dispatch_retain", true)
.Case("dispatch_release", true)
.Case("xpc_retain", true)
.Case("xpc_release", true)
.Default(false);
if (!isGCDOrXPC)
return;
StmtExpr *StmtE = nullptr;
Stmt *S = Msg;
while (S) {
if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) {
StmtE = SE;
break;
}
S = StmtMap->getParent(S);
}
if (!StmtE)
return;
Stmt::child_range StmtExprChild = StmtE->children();
if (StmtExprChild.begin() == StmtExprChild.end())
return;
auto *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild.begin());
if (!CompS)
return;
Stmt::child_range CompStmtChild = CompS->children();
if (CompStmtChild.begin() == CompStmtChild.end())
return;
auto *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild.begin());
if (!DeclS)
return;
if (!DeclS->isSingleDecl())
return;
VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl());
if (!VD)
return;
Expr *Init = VD->getInit();
if (!Init)
return;
RecContainer = StmtE;
Rec = Init->IgnoreParenImpCasts();
if (FullExpr *FE = dyn_cast<FullExpr>(Rec))
Rec = FE->getSubExpr()->IgnoreParenImpCasts();
RecRange = Rec->getSourceRange();
if (SM.isMacroArgExpansion(RecRange.getBegin()))
RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin()));
if (SM.isMacroArgExpansion(RecRange.getEnd()))
RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd()));
}
void clearDiagnostics(SourceLocation loc) const {
Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
diag::err_unavailable,
diag::err_unavailable_message,
loc);
}
bool isDelegateMessage(Expr *E) const {
if (!E) return false;
E = E->IgnoreParenCasts();
// Also look through property-getter sugar.
if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E))
E = pseudoOp->getResultExpr()->IgnoreImplicit();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
return false;
}
bool isInAtFinally(Expr *E) const {
assert(E);
Stmt *S = E;
while (S) {
if (isa<ObjCAtFinallyStmt>(S))
return true;
S = StmtMap->getParent(S);
}
return false;
}
bool isRemovable(Expr *E) const {
return Removables.count(E);
}
bool tryRemoving(Expr *E) const {
if (isRemovable(E)) {
Pass.TA.removeStmt(E);
return true;
}
Stmt *parent = StmtMap->getParent(E);
if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
return tryRemoving(castE);
if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
return tryRemoving(parenE);
if (BinaryOperator *
bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
isRemovable(bopE)) {
Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
return true;
}
}
return false;
}
};
} // anonymous namespace
void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {
BodyTransform<RetainReleaseDeallocRemover> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,466 +0,0 @@
//===--- TransUnbridgedCasts.cpp - Transformations to ARC mode ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// rewriteUnbridgedCasts:
//
// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer
// is from a file-level variable, __bridge cast is used to convert it.
// For the result of a function call that we know is +1/+0,
// __bridge/CFBridgingRelease is used.
//
// NSString *str = (NSString *)kUTTypePlainText;
// str = b ? kUTTypeRTF : kUTTypePlainText;
// NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault,
// _uuid);
// ---->
// NSString *str = (__bridge NSString *)kUTTypePlainText;
// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
// NSString *_uuidString = (NSString *)
// CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
//
// For a C pointer to ObjC, for casting 'self', __bridge is used.
//
// CFStringRef str = (CFStringRef)self;
// ---->
// CFStringRef str = (__bridge CFStringRef)self;
//
// Uses of Block_copy/Block_release macros are rewritten:
//
// c = Block_copy(b);
// Block_release(c);
// ---->
// c = [b copy];
// <removed>
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/ParentMap.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
MigrationPass &Pass;
IdentifierInfo *SelfII;
std::unique_ptr<ParentMap> StmtMap;
Decl *ParentD;
Stmt *Body;
mutable std::unique_ptr<ExprSet> Removables;
public:
UnbridgedCastRewriter(MigrationPass &pass)
: Pass(pass), ParentD(nullptr), Body(nullptr) {
SelfII = &Pass.Ctx.Idents.get("self");
}
void transformBody(Stmt *body, Decl *ParentD) {
this->ParentD = ParentD;
Body = body;
StmtMap.reset(new ParentMap(body));
TraverseStmt(body);
}
bool TraverseBlockDecl(BlockDecl *D) {
// ParentMap does not enter into a BlockDecl to record its stmts, so use a
// new UnbridgedCastRewriter to handle the block.
UnbridgedCastRewriter(Pass).transformBody(D->getBody(), D);
return true;
}
bool VisitCastExpr(CastExpr *E) {
if (E->getCastKind() != CK_CPointerToObjCPointerCast &&
E->getCastKind() != CK_BitCast &&
E->getCastKind() != CK_AnyPointerToBlockPointerCast)
return true;
QualType castType = E->getType();
Expr *castExpr = E->getSubExpr();
QualType castExprType = castExpr->getType();
if (castType->isObjCRetainableType() == castExprType->isObjCRetainableType())
return true;
bool exprRetainable = castExprType->isObjCIndirectLifetimeType();
bool castRetainable = castType->isObjCIndirectLifetimeType();
if (exprRetainable == castRetainable) return true;
if (castExpr->isNullPointerConstant(Pass.Ctx,
Expr::NPC_ValueDependentIsNull))
return true;
SourceLocation loc = castExpr->getExprLoc();
if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc))
return true;
if (castType->isObjCRetainableType())
transformNonObjCToObjCCast(E);
else
transformObjCToNonObjCCast(E);
return true;
}
private:
void transformNonObjCToObjCCast(CastExpr *E) {
if (!E) return;
// Global vars are assumed that are cast as unretained.
if (isGlobalVar(E))
if (E->getSubExpr()->getType()->isPointerType()) {
castToObjCObject(E, /*retained=*/false);
return;
}
// If the cast is directly over the result of a Core Foundation function
// try to figure out whether it should be cast as retained or unretained.
Expr *inner = E->IgnoreParenCasts();
if (CallExpr *callE = dyn_cast<CallExpr>(inner)) {
if (FunctionDecl *FD = callE->getDirectCallee()) {
if (FD->hasAttr<CFReturnsRetainedAttr>()) {
castToObjCObject(E, /*retained=*/true);
return;
}
if (FD->hasAttr<CFReturnsNotRetainedAttr>()) {
castToObjCObject(E, /*retained=*/false);
return;
}
if (FD->isGlobal() &&
FD->getIdentifier() &&
ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF",
FD->getIdentifier()->getName())) {
StringRef fname = FD->getIdentifier()->getName();
if (fname.ends_with("Retain") || fname.contains("Create") ||
fname.contains("Copy")) {
// Do not migrate to couple of bridge transfer casts which
// cancel each other out. Leave it unchanged so error gets user
// attention instead.
if (FD->getName() == "CFRetain" &&
FD->getNumParams() == 1 &&
FD->getParent()->isTranslationUnit() &&
FD->isExternallyVisible()) {
Expr *Arg = callE->getArg(0);
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
const Expr *sub = ICE->getSubExpr();
QualType T = sub->getType();
if (T->isObjCObjectPointerType())
return;
}
}
castToObjCObject(E, /*retained=*/true);
return;
}
if (fname.contains("Get")) {
castToObjCObject(E, /*retained=*/false);
return;
}
}
}
}
// If returning an ivar or a member of an ivar from a +0 method, use
// a __bridge cast.
Expr *base = inner->IgnoreParenImpCasts();
while (isa<MemberExpr>(base))
base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts();
if (isa<ObjCIvarRefExpr>(base) &&
isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) {
if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) {
if (!method->hasAttr<NSReturnsRetainedAttr>()) {
castToObjCObject(E, /*retained=*/false);
return;
}
}
}
}
void castToObjCObject(CastExpr *E, bool retained) {
rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge);
}
void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) {
Transaction Trans(Pass.TA);
rewriteToBridgedCast(E, Kind, Trans);
}
void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind,
Transaction &Trans) {
TransformActions &TA = Pass.TA;
// We will remove the compiler diagnostic.
if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
E->getBeginLoc())) {
Trans.abort();
return;
}
StringRef bridge;
switch(Kind) {
case OBC_Bridge:
bridge = "__bridge "; break;
case OBC_BridgeTransfer:
bridge = "__bridge_transfer "; break;
case OBC_BridgeRetained:
bridge = "__bridge_retained "; break;
}
TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge, E->getBeginLoc());
if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) {
if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) {
TA.insertAfterToken(CCE->getLParenLoc(), bridge);
} else {
SourceLocation insertLoc = E->getSubExpr()->getBeginLoc();
SmallString<128> newCast;
newCast += '(';
newCast += bridge;
newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());
newCast += ')';
if (isa<ParenExpr>(E->getSubExpr())) {
TA.insert(insertLoc, newCast.str());
} else {
newCast += '(';
TA.insert(insertLoc, newCast.str());
TA.insertAfterToken(E->getEndLoc(), ")");
}
}
} else {
assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained);
SmallString<32> BridgeCall;
Expr *WrapE = E->getSubExpr();
SourceLocation InsertLoc = WrapE->getBeginLoc();
SourceManager &SM = Pass.Ctx.getSourceManager();
char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1));
if (Lexer::isAsciiIdentifierContinueChar(PrevChar,
Pass.Ctx.getLangOpts()))
BridgeCall += ' ';
if (Kind == OBC_BridgeTransfer)
BridgeCall += "CFBridgingRelease";
else
BridgeCall += "CFBridgingRetain";
if (isa<ParenExpr>(WrapE)) {
TA.insert(InsertLoc, BridgeCall);
} else {
BridgeCall += '(';
TA.insert(InsertLoc, BridgeCall);
TA.insertAfterToken(WrapE->getEndLoc(), ")");
}
}
}
void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) {
Transaction Trans(Pass.TA);
Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange());
rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans);
}
void getBlockMacroRanges(CastExpr *E, SourceRange &Outer, SourceRange &Inner) {
SourceManager &SM = Pass.Ctx.getSourceManager();
SourceLocation Loc = E->getExprLoc();
assert(Loc.isMacroID());
CharSourceRange MacroRange = SM.getImmediateExpansionRange(Loc);
SourceRange SubRange = E->getSubExpr()->IgnoreParenImpCasts()->getSourceRange();
SourceLocation InnerBegin = SM.getImmediateMacroCallerLoc(SubRange.getBegin());
SourceLocation InnerEnd = SM.getImmediateMacroCallerLoc(SubRange.getEnd());
Outer = MacroRange.getAsRange();
Inner = SourceRange(InnerBegin, InnerEnd);
}
void rewriteBlockCopyMacro(CastExpr *E) {
SourceRange OuterRange, InnerRange;
getBlockMacroRanges(E, OuterRange, InnerRange);
Transaction Trans(Pass.TA);
Pass.TA.replace(OuterRange, InnerRange);
Pass.TA.insert(InnerRange.getBegin(), "[");
Pass.TA.insertAfterToken(InnerRange.getEnd(), " copy]");
Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
OuterRange);
}
void removeBlockReleaseMacro(CastExpr *E) {
SourceRange OuterRange, InnerRange;
getBlockMacroRanges(E, OuterRange, InnerRange);
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast,
diag::err_arc_cast_requires_bridge,
OuterRange);
if (!hasSideEffects(E, Pass.Ctx)) {
if (tryRemoving(cast<Expr>(StmtMap->getParentIgnoreParenCasts(E))))
return;
}
Pass.TA.replace(OuterRange, InnerRange);
}
bool tryRemoving(Expr *E) const {
if (!Removables) {
Removables.reset(new ExprSet);
collectRemovables(Body, *Removables);
}
if (Removables->count(E)) {
Pass.TA.removeStmt(E);
return true;
}
return false;
}
void transformObjCToNonObjCCast(CastExpr *E) {
SourceLocation CastLoc = E->getExprLoc();
if (CastLoc.isMacroID()) {
StringRef MacroName = Lexer::getImmediateMacroName(CastLoc,
Pass.Ctx.getSourceManager(),
Pass.Ctx.getLangOpts());
if (MacroName == "Block_copy") {
rewriteBlockCopyMacro(E);
return;
}
if (MacroName == "Block_release") {
removeBlockReleaseMacro(E);
return;
}
}
if (isSelf(E->getSubExpr()))
return rewriteToBridgedCast(E, OBC_Bridge);
CallExpr *callE;
if (isPassedToCFRetain(E, callE))
return rewriteCastForCFRetain(E, callE);
ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr());
if (family == OMF_retain)
return rewriteToBridgedCast(E, OBC_BridgeRetained);
if (family == OMF_autorelease || family == OMF_release) {
std::string err = "it is not safe to cast to '";
err += E->getType().getAsString(Pass.Ctx.getPrintingPolicy());
err += "' the result of '";
err += family == OMF_autorelease ? "autorelease" : "release";
err += "' message; a __bridge cast may result in a pointer to a "
"destroyed object and a __bridge_retained may leak the object";
Pass.TA.reportError(err, E->getBeginLoc(),
E->getSubExpr()->getSourceRange());
Stmt *parent = E;
do {
parent = StmtMap->getParentIgnoreParenImpCasts(parent);
} while (isa_and_nonnull<FullExpr>(parent));
if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) {
std::string note = "remove the cast and change return type of function "
"to '";
note += E->getSubExpr()->getType().getAsString(Pass.Ctx.getPrintingPolicy());
note += "' to have the object automatically autoreleased";
Pass.TA.reportNote(note, retS->getBeginLoc());
}
}
Expr *subExpr = E->getSubExpr();
// Look through pseudo-object expressions.
if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(subExpr)) {
subExpr = pseudo->getResultExpr();
assert(subExpr && "no result for pseudo-object of non-void type?");
}
if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(subExpr)) {
if (implCE->getCastKind() == CK_ARCConsumeObject)
return rewriteToBridgedCast(E, OBC_BridgeRetained);
if (implCE->getCastKind() == CK_ARCReclaimReturnedObject)
return rewriteToBridgedCast(E, OBC_Bridge);
}
bool isConsumed = false;
if (isPassedToCParamWithKnownOwnership(E, isConsumed))
return rewriteToBridgedCast(E, isConsumed ? OBC_BridgeRetained
: OBC_Bridge);
}
static ObjCMethodFamily getFamilyOfMessage(Expr *E) {
E = E->IgnoreParenCasts();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
return ME->getMethodFamily();
return OMF_None;
}
bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const {
if ((callE = dyn_cast_or_null<CallExpr>(
StmtMap->getParentIgnoreParenImpCasts(E))))
if (FunctionDecl *
FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl()))
if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 &&
FD->getParent()->isTranslationUnit() &&
FD->isExternallyVisible())
return true;
return false;
}
bool isPassedToCParamWithKnownOwnership(Expr *E, bool &isConsumed) const {
if (CallExpr *callE = dyn_cast_or_null<CallExpr>(
StmtMap->getParentIgnoreParenImpCasts(E)))
if (FunctionDecl *
FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) {
unsigned i = 0;
for (unsigned e = callE->getNumArgs(); i != e; ++i) {
Expr *arg = callE->getArg(i);
if (arg == E || arg->IgnoreParenImpCasts() == E)
break;
}
if (i < callE->getNumArgs() && i < FD->getNumParams()) {
ParmVarDecl *PD = FD->getParamDecl(i);
if (PD->hasAttr<CFConsumedAttr>()) {
isConsumed = true;
return true;
}
}
}
return false;
}
bool isSelf(Expr *E) const {
E = E->IgnoreParenLValueCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
if (IPD->getIdentifier() == SelfII)
return true;
return false;
}
};
} // end anonymous namespace
void trans::rewriteUnbridgedCasts(MigrationPass &pass) {
BodyTransform<UnbridgedCastRewriter> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,77 +0,0 @@
//===--- TransUnusedInitDelegate.cpp - Transformations to ARC mode --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Transformations:
//===----------------------------------------------------------------------===//
//
// rewriteUnusedInitDelegate:
//
// Rewrites an unused result of calling a delegate initialization, to assigning
// the result to self.
// e.g
// [self init];
// ---->
// self = [self init];
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class UnusedInitRewriter : public RecursiveASTVisitor<UnusedInitRewriter> {
Stmt *Body;
MigrationPass &Pass;
ExprSet Removables;
public:
UnusedInitRewriter(MigrationPass &pass)
: Body(nullptr), Pass(pass) { }
void transformBody(Stmt *body, Decl *ParentD) {
Body = body;
collectRemovables(body, Removables);
TraverseStmt(body);
}
bool VisitObjCMessageExpr(ObjCMessageExpr *ME) {
if (ME->isDelegateInitCall() &&
isRemovable(ME) &&
Pass.TA.hasDiagnostic(diag::err_arc_unused_init_message,
ME->getExprLoc())) {
Transaction Trans(Pass.TA);
Pass.TA.clearDiagnostic(diag::err_arc_unused_init_message,
ME->getExprLoc());
SourceRange ExprRange = ME->getSourceRange();
Pass.TA.insert(ExprRange.getBegin(), "if (!(self = ");
std::string retStr = ")) return ";
retStr += getNilString(Pass);
Pass.TA.insertAfterToken(ExprRange.getEnd(), retStr);
}
return true;
}
private:
bool isRemovable(Expr *E) const {
return Removables.count(E);
}
};
} // anonymous namespace
void trans::rewriteUnusedInitDelegate(MigrationPass &pass) {
BodyTransform<UnusedInitRewriter> trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,224 +0,0 @@
//===--- TransZeroOutPropsInDealloc.cpp - Transformations to ARC mode -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// removeZeroOutPropsInDealloc:
//
// Removes zero'ing out "strong" @synthesized properties in a -dealloc method.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
namespace {
class ZeroOutInDeallocRemover :
public RecursiveASTVisitor<ZeroOutInDeallocRemover> {
typedef RecursiveASTVisitor<ZeroOutInDeallocRemover> base;
MigrationPass &Pass;
llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*> SynthesizedProperties;
ImplicitParamDecl *SelfD;
ExprSet Removables;
Selector FinalizeSel;
public:
ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(nullptr) {
FinalizeSel =
Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
}
bool VisitObjCMessageExpr(ObjCMessageExpr *ME) {
ASTContext &Ctx = Pass.Ctx;
TransformActions &TA = Pass.TA;
if (ME->getReceiverKind() != ObjCMessageExpr::Instance)
return true;
Expr *receiver = ME->getInstanceReceiver();
if (!receiver)
return true;
DeclRefExpr *refE = dyn_cast<DeclRefExpr>(receiver->IgnoreParenCasts());
if (!refE || refE->getDecl() != SelfD)
return true;
bool BackedBySynthesizeSetter = false;
for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
P = SynthesizedProperties.begin(),
E = SynthesizedProperties.end(); P != E; ++P) {
ObjCPropertyDecl *PropDecl = P->first;
if (PropDecl->getSetterName() == ME->getSelector()) {
BackedBySynthesizeSetter = true;
break;
}
}
if (!BackedBySynthesizeSetter)
return true;
// Remove the setter message if RHS is null
Transaction Trans(TA);
Expr *RHS = ME->getArg(0);
bool RHSIsNull =
RHS->isNullPointerConstant(Ctx,
Expr::NPC_ValueDependentIsNull);
if (RHSIsNull && isRemovable(ME))
TA.removeStmt(ME);
return true;
}
bool VisitPseudoObjectExpr(PseudoObjectExpr *POE) {
if (isZeroingPropIvar(POE) && isRemovable(POE)) {
Transaction Trans(Pass.TA);
Pass.TA.removeStmt(POE);
}
return true;
}
bool VisitBinaryOperator(BinaryOperator *BOE) {
if (isZeroingPropIvar(BOE) && isRemovable(BOE)) {
Transaction Trans(Pass.TA);
Pass.TA.removeStmt(BOE);
}
return true;
}
bool TraverseObjCMethodDecl(ObjCMethodDecl *D) {
if (D->getMethodFamily() != OMF_dealloc &&
!(D->isInstanceMethod() && D->getSelector() == FinalizeSel))
return true;
if (!D->hasBody())
return true;
ObjCImplDecl *IMD = dyn_cast<ObjCImplDecl>(D->getDeclContext());
if (!IMD)
return true;
SelfD = D->getSelfDecl();
collectRemovables(D->getBody(), Removables);
// For a 'dealloc' method use, find all property implementations in
// this class implementation.
for (auto *PID : IMD->property_impls()) {
if (PID->getPropertyImplementation() ==
ObjCPropertyImplDecl::Synthesize) {
ObjCPropertyDecl *PD = PID->getPropertyDecl();
ObjCMethodDecl *setterM = PD->getSetterMethodDecl();
if (!(setterM && setterM->isDefined())) {
ObjCPropertyAttribute::Kind AttrKind = PD->getPropertyAttributes();
if (AttrKind & (ObjCPropertyAttribute::kind_retain |
ObjCPropertyAttribute::kind_copy |
ObjCPropertyAttribute::kind_strong))
SynthesizedProperties[PD] = PID;
}
}
}
// Now, remove all zeroing of ivars etc.
base::TraverseObjCMethodDecl(D);
// clear out for next method.
SynthesizedProperties.clear();
SelfD = nullptr;
Removables.clear();
return true;
}
bool TraverseFunctionDecl(FunctionDecl *D) { return true; }
bool TraverseBlockDecl(BlockDecl *block) { return true; }
bool TraverseBlockExpr(BlockExpr *block) { return true; }
private:
bool isRemovable(Expr *E) const {
return Removables.count(E);
}
bool isZeroingPropIvar(Expr *E) {
E = E->IgnoreParens();
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
return isZeroingPropIvar(BO);
if (PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(E))
return isZeroingPropIvar(PO);
return false;
}
bool isZeroingPropIvar(BinaryOperator *BOE) {
if (BOE->getOpcode() == BO_Comma)
return isZeroingPropIvar(BOE->getLHS()) &&
isZeroingPropIvar(BOE->getRHS());
if (BOE->getOpcode() != BO_Assign)
return false;
Expr *LHS = BOE->getLHS();
if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) {
ObjCIvarDecl *IVDecl = IV->getDecl();
if (!IVDecl->getType()->isObjCObjectPointerType())
return false;
bool IvarBacksPropertySynthesis = false;
for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
P = SynthesizedProperties.begin(),
E = SynthesizedProperties.end(); P != E; ++P) {
ObjCPropertyImplDecl *PropImpDecl = P->second;
if (PropImpDecl && PropImpDecl->getPropertyIvarDecl() == IVDecl) {
IvarBacksPropertySynthesis = true;
break;
}
}
if (!IvarBacksPropertySynthesis)
return false;
}
else
return false;
return isZero(BOE->getRHS());
}
bool isZeroingPropIvar(PseudoObjectExpr *PO) {
BinaryOperator *BO = dyn_cast<BinaryOperator>(PO->getSyntacticForm());
if (!BO) return false;
if (BO->getOpcode() != BO_Assign) return false;
ObjCPropertyRefExpr *PropRefExp =
dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParens());
if (!PropRefExp) return false;
// TODO: Using implicit property decl.
if (PropRefExp->isImplicitProperty())
return false;
if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) {
if (!SynthesizedProperties.count(PDecl))
return false;
}
return isZero(cast<OpaqueValueExpr>(BO->getRHS())->getSourceExpr());
}
bool isZero(Expr *E) {
if (E->isNullPointerConstant(Pass.Ctx, Expr::NPC_ValueDependentIsNull))
return true;
return isZeroingPropIvar(E);
}
};
} // anonymous namespace
void trans::removeZeroOutPropsInDeallocFinalize(MigrationPass &pass) {
ZeroOutInDeallocRemover trans(pass);
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
}

@ -1,700 +0,0 @@
//===-- TransformActions.cpp - Migration to ARC mode ----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/DenseSet.h"
#include <map>
using namespace clang;
using namespace arcmt;
namespace {
/// Collects transformations and merges them before applying them with
/// with applyRewrites(). E.g. if the same source range
/// is requested to be removed twice, only one rewriter remove will be invoked.
/// Rewrites happen in "transactions"; if one rewrite in the transaction cannot
/// be done (e.g. it resides in a macro) all rewrites in the transaction are
/// aborted.
/// FIXME: "Transactional" rewrites support should be baked in the Rewriter.
class TransformActionsImpl {
CapturedDiagList &CapturedDiags;
ASTContext &Ctx;
Preprocessor &PP;
bool IsInTransaction;
enum ActionKind {
Act_Insert, Act_InsertAfterToken,
Act_Remove, Act_RemoveStmt,
Act_Replace, Act_ReplaceText,
Act_IncreaseIndentation,
Act_ClearDiagnostic
};
struct ActionData {
ActionKind Kind;
SourceLocation Loc;
SourceRange R1, R2;
StringRef Text1, Text2;
Stmt *S;
SmallVector<unsigned, 2> DiagIDs;
};
std::vector<ActionData> CachedActions;
enum RangeComparison {
Range_Before,
Range_After,
Range_Contains,
Range_Contained,
Range_ExtendsBegin,
Range_ExtendsEnd
};
/// A range to remove. It is a character range.
struct CharRange {
FullSourceLoc Begin, End;
CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) {
SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd();
assert(beginLoc.isValid() && endLoc.isValid());
if (range.isTokenRange()) {
Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);
End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr);
} else {
Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);
End = FullSourceLoc(srcMgr.getExpansionLoc(endLoc), srcMgr);
}
assert(Begin.isValid() && End.isValid());
}
RangeComparison compareWith(const CharRange &RHS) const {
if (End.isBeforeInTranslationUnitThan(RHS.Begin))
return Range_Before;
if (RHS.End.isBeforeInTranslationUnitThan(Begin))
return Range_After;
if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
!RHS.End.isBeforeInTranslationUnitThan(End))
return Range_Contained;
if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
RHS.End.isBeforeInTranslationUnitThan(End))
return Range_Contains;
if (Begin.isBeforeInTranslationUnitThan(RHS.Begin))
return Range_ExtendsBegin;
else
return Range_ExtendsEnd;
}
static RangeComparison compare(SourceRange LHS, SourceRange RHS,
SourceManager &SrcMgr, Preprocessor &PP) {
return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP)
.compareWith(CharRange(CharSourceRange::getTokenRange(RHS),
SrcMgr, PP));
}
};
typedef SmallVector<StringRef, 2> TextsVec;
typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare>
InsertsMap;
InsertsMap Inserts;
/// A list of ranges to remove. They are always sorted and they never
/// intersect with each other.
std::list<CharRange> Removals;
llvm::DenseSet<Stmt *> StmtRemovals;
std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges;
/// Keeps text passed to transformation methods.
llvm::StringMap<bool> UniqueText;
public:
TransformActionsImpl(CapturedDiagList &capturedDiags,
ASTContext &ctx, Preprocessor &PP)
: CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { }
ASTContext &getASTContext() { return Ctx; }
void startTransaction();
bool commitTransaction();
void abortTransaction();
bool isInTransaction() const { return IsInTransaction; }
void insert(SourceLocation loc, StringRef text);
void insertAfterToken(SourceLocation loc, StringRef text);
void remove(SourceRange range);
void removeStmt(Stmt *S);
void replace(SourceRange range, StringRef text);
void replace(SourceRange range, SourceRange replacementRange);
void replaceStmt(Stmt *S, StringRef text);
void replaceText(SourceLocation loc, StringRef text,
StringRef replacementText);
void increaseIndentation(SourceRange range,
SourceLocation parentIndent);
bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
void applyRewrites(TransformActions::RewriteReceiver &receiver);
private:
bool canInsert(SourceLocation loc);
bool canInsertAfterToken(SourceLocation loc);
bool canRemoveRange(SourceRange range);
bool canReplaceRange(SourceRange range, SourceRange replacementRange);
bool canReplaceText(SourceLocation loc, StringRef text);
void commitInsert(SourceLocation loc, StringRef text);
void commitInsertAfterToken(SourceLocation loc, StringRef text);
void commitRemove(SourceRange range);
void commitRemoveStmt(Stmt *S);
void commitReplace(SourceRange range, SourceRange replacementRange);
void commitReplaceText(SourceLocation loc, StringRef text,
StringRef replacementText);
void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent);
void commitClearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
void addRemoval(CharSourceRange range);
void addInsertion(SourceLocation loc, StringRef text);
/// Stores text passed to the transformation methods to keep the string
/// "alive". Since the vast majority of text will be the same, we also unique
/// the strings using a StringMap.
StringRef getUniqueText(StringRef text);
/// Computes the source location just past the end of the token at
/// the given source location. If the location points at a macro, the whole
/// macro expansion is skipped.
static SourceLocation getLocForEndOfToken(SourceLocation loc,
SourceManager &SM,Preprocessor &PP);
};
} // anonymous namespace
void TransformActionsImpl::startTransaction() {
assert(!IsInTransaction &&
"Cannot start a transaction in the middle of another one");
IsInTransaction = true;
}
bool TransformActionsImpl::commitTransaction() {
assert(IsInTransaction && "No transaction started");
if (CachedActions.empty()) {
IsInTransaction = false;
return false;
}
// Verify that all actions are possible otherwise abort the whole transaction.
bool AllActionsPossible = true;
for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
ActionData &act = CachedActions[i];
switch (act.Kind) {
case Act_Insert:
if (!canInsert(act.Loc))
AllActionsPossible = false;
break;
case Act_InsertAfterToken:
if (!canInsertAfterToken(act.Loc))
AllActionsPossible = false;
break;
case Act_Remove:
if (!canRemoveRange(act.R1))
AllActionsPossible = false;
break;
case Act_RemoveStmt:
assert(act.S);
if (!canRemoveRange(act.S->getSourceRange()))
AllActionsPossible = false;
break;
case Act_Replace:
if (!canReplaceRange(act.R1, act.R2))
AllActionsPossible = false;
break;
case Act_ReplaceText:
if (!canReplaceText(act.Loc, act.Text1))
AllActionsPossible = false;
break;
case Act_IncreaseIndentation:
// This is not important, we don't care if it will fail.
break;
case Act_ClearDiagnostic:
// We are just checking source rewrites.
break;
}
if (!AllActionsPossible)
break;
}
if (!AllActionsPossible) {
abortTransaction();
return true;
}
for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
ActionData &act = CachedActions[i];
switch (act.Kind) {
case Act_Insert:
commitInsert(act.Loc, act.Text1);
break;
case Act_InsertAfterToken:
commitInsertAfterToken(act.Loc, act.Text1);
break;
case Act_Remove:
commitRemove(act.R1);
break;
case Act_RemoveStmt:
commitRemoveStmt(act.S);
break;
case Act_Replace:
commitReplace(act.R1, act.R2);
break;
case Act_ReplaceText:
commitReplaceText(act.Loc, act.Text1, act.Text2);
break;
case Act_IncreaseIndentation:
commitIncreaseIndentation(act.R1, act.Loc);
break;
case Act_ClearDiagnostic:
commitClearDiagnostic(act.DiagIDs, act.R1);
break;
}
}
CachedActions.clear();
IsInTransaction = false;
return false;
}
void TransformActionsImpl::abortTransaction() {
assert(IsInTransaction && "No transaction started");
CachedActions.clear();
IsInTransaction = false;
}
void TransformActionsImpl::insert(SourceLocation loc, StringRef text) {
assert(IsInTransaction && "Actions only allowed during a transaction");
text = getUniqueText(text);
ActionData data;
data.Kind = Act_Insert;
data.Loc = loc;
data.Text1 = text;
CachedActions.push_back(data);
}
void TransformActionsImpl::insertAfterToken(SourceLocation loc, StringRef text) {
assert(IsInTransaction && "Actions only allowed during a transaction");
text = getUniqueText(text);
ActionData data;
data.Kind = Act_InsertAfterToken;
data.Loc = loc;
data.Text1 = text;
CachedActions.push_back(data);
}
void TransformActionsImpl::remove(SourceRange range) {
assert(IsInTransaction && "Actions only allowed during a transaction");
ActionData data;
data.Kind = Act_Remove;
data.R1 = range;
CachedActions.push_back(data);
}
void TransformActionsImpl::removeStmt(Stmt *S) {
assert(IsInTransaction && "Actions only allowed during a transaction");
ActionData data;
data.Kind = Act_RemoveStmt;
if (auto *E = dyn_cast<Expr>(S))
S = E->IgnoreImplicit(); // important for uniquing
data.S = S;
CachedActions.push_back(data);
}
void TransformActionsImpl::replace(SourceRange range, StringRef text) {
assert(IsInTransaction && "Actions only allowed during a transaction");
text = getUniqueText(text);
remove(range);
insert(range.getBegin(), text);
}
void TransformActionsImpl::replace(SourceRange range,
SourceRange replacementRange) {
assert(IsInTransaction && "Actions only allowed during a transaction");
ActionData data;
data.Kind = Act_Replace;
data.R1 = range;
data.R2 = replacementRange;
CachedActions.push_back(data);
}
void TransformActionsImpl::replaceText(SourceLocation loc, StringRef text,
StringRef replacementText) {
text = getUniqueText(text);
replacementText = getUniqueText(replacementText);
ActionData data;
data.Kind = Act_ReplaceText;
data.Loc = loc;
data.Text1 = text;
data.Text2 = replacementText;
CachedActions.push_back(data);
}
void TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) {
assert(IsInTransaction && "Actions only allowed during a transaction");
text = getUniqueText(text);
insert(S->getBeginLoc(), text);
removeStmt(S);
}
void TransformActionsImpl::increaseIndentation(SourceRange range,
SourceLocation parentIndent) {
if (range.isInvalid()) return;
assert(IsInTransaction && "Actions only allowed during a transaction");
ActionData data;
data.Kind = Act_IncreaseIndentation;
data.R1 = range;
data.Loc = parentIndent;
CachedActions.push_back(data);
}
bool TransformActionsImpl::clearDiagnostic(ArrayRef<unsigned> IDs,
SourceRange range) {
assert(IsInTransaction && "Actions only allowed during a transaction");
if (!CapturedDiags.hasDiagnostic(IDs, range))
return false;
ActionData data;
data.Kind = Act_ClearDiagnostic;
data.R1 = range;
data.DiagIDs.append(IDs.begin(), IDs.end());
CachedActions.push_back(data);
return true;
}
bool TransformActionsImpl::canInsert(SourceLocation loc) {
if (loc.isInvalid())
return false;
SourceManager &SM = Ctx.getSourceManager();
if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))
return false;
if (loc.isFileID())
return true;
return PP.isAtStartOfMacroExpansion(loc);
}
bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
if (loc.isInvalid())
return false;
SourceManager &SM = Ctx.getSourceManager();
if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))
return false;
if (loc.isFileID())
return true;
return PP.isAtEndOfMacroExpansion(loc);
}
bool TransformActionsImpl::canRemoveRange(SourceRange range) {
return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd());
}
bool TransformActionsImpl::canReplaceRange(SourceRange range,
SourceRange replacementRange) {
return canRemoveRange(range) && canRemoveRange(replacementRange);
}
bool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) {
if (!canInsert(loc))
return false;
SourceManager &SM = Ctx.getSourceManager();
loc = SM.getExpansionLoc(loc);
// Break down the source location.
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
// Try to load the file buffer.
bool invalidTemp = false;
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
if (invalidTemp)
return false;
return file.substr(locInfo.second).starts_with(text);
}
void TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) {
addInsertion(loc, text);
}
void TransformActionsImpl::commitInsertAfterToken(SourceLocation loc,
StringRef text) {
addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text);
}
void TransformActionsImpl::commitRemove(SourceRange range) {
addRemoval(CharSourceRange::getTokenRange(range));
}
void TransformActionsImpl::commitRemoveStmt(Stmt *S) {
assert(S);
if (StmtRemovals.count(S))
return; // already removed.
if (Expr *E = dyn_cast<Expr>(S)) {
commitRemove(E->getSourceRange());
commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName());
} else
commitRemove(S->getSourceRange());
StmtRemovals.insert(S);
}
void TransformActionsImpl::commitReplace(SourceRange range,
SourceRange replacementRange) {
RangeComparison comp = CharRange::compare(replacementRange, range,
Ctx.getSourceManager(), PP);
assert(comp == Range_Contained);
if (comp != Range_Contained)
return; // Although we asserted, be extra safe for release build.
if (range.getBegin() != replacementRange.getBegin())
addRemoval(CharSourceRange::getCharRange(range.getBegin(),
replacementRange.getBegin()));
if (replacementRange.getEnd() != range.getEnd())
addRemoval(CharSourceRange::getTokenRange(
getLocForEndOfToken(replacementRange.getEnd(),
Ctx.getSourceManager(), PP),
range.getEnd()));
}
void TransformActionsImpl::commitReplaceText(SourceLocation loc,
StringRef text,
StringRef replacementText) {
SourceManager &SM = Ctx.getSourceManager();
loc = SM.getExpansionLoc(loc);
// canReplaceText already checked if loc points at text.
SourceLocation afterText = loc.getLocWithOffset(text.size());
addRemoval(CharSourceRange::getCharRange(loc, afterText));
commitInsert(loc, replacementText);
}
void TransformActionsImpl::commitIncreaseIndentation(SourceRange range,
SourceLocation parentIndent) {
SourceManager &SM = Ctx.getSourceManager();
IndentationRanges.push_back(
std::make_pair(CharRange(CharSourceRange::getTokenRange(range),
SM, PP),
SM.getExpansionLoc(parentIndent)));
}
void TransformActionsImpl::commitClearDiagnostic(ArrayRef<unsigned> IDs,
SourceRange range) {
CapturedDiags.clearDiagnostic(IDs, range);
}
void TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) {
SourceManager &SM = Ctx.getSourceManager();
loc = SM.getExpansionLoc(loc);
for (const CharRange &I : llvm::reverse(Removals)) {
if (!SM.isBeforeInTranslationUnit(loc, I.End))
break;
if (I.Begin.isBeforeInTranslationUnitThan(loc))
return;
}
Inserts[FullSourceLoc(loc, SM)].push_back(text);
}
void TransformActionsImpl::addRemoval(CharSourceRange range) {
CharRange newRange(range, Ctx.getSourceManager(), PP);
if (newRange.Begin == newRange.End)
return;
Inserts.erase(Inserts.upper_bound(newRange.Begin),
Inserts.lower_bound(newRange.End));
std::list<CharRange>::iterator I = Removals.end();
while (I != Removals.begin()) {
std::list<CharRange>::iterator RI = I;
--RI;
RangeComparison comp = newRange.compareWith(*RI);
switch (comp) {
case Range_Before:
--I;
break;
case Range_After:
Removals.insert(I, newRange);
return;
case Range_Contained:
return;
case Range_Contains:
RI->End = newRange.End;
[[fallthrough]];
case Range_ExtendsBegin:
newRange.End = RI->End;
Removals.erase(RI);
break;
case Range_ExtendsEnd:
RI->End = newRange.End;
return;
}
}
Removals.insert(Removals.begin(), newRange);
}
void TransformActionsImpl::applyRewrites(
TransformActions::RewriteReceiver &receiver) {
for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) {
SourceLocation loc = I->first;
for (TextsVec::iterator
TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) {
receiver.insert(loc, *TI);
}
}
for (std::vector<std::pair<CharRange, SourceLocation> >::iterator
I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) {
CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin,
I->first.End);
receiver.increaseIndentation(range, I->second);
}
for (std::list<CharRange>::iterator
I = Removals.begin(), E = Removals.end(); I != E; ++I) {
CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End);
receiver.remove(range);
}
}
/// Stores text passed to the transformation methods to keep the string
/// "alive". Since the vast majority of text will be the same, we also unique
/// the strings using a StringMap.
StringRef TransformActionsImpl::getUniqueText(StringRef text) {
return UniqueText.insert(std::make_pair(text, false)).first->first();
}
/// Computes the source location just past the end of the token at
/// the given source location. If the location points at a macro, the whole
/// macro expansion is skipped.
SourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc,
SourceManager &SM,
Preprocessor &PP) {
if (loc.isMacroID()) {
CharSourceRange Exp = SM.getExpansionRange(loc);
if (Exp.isCharRange())
return Exp.getEnd();
loc = Exp.getEnd();
}
return PP.getLocForEndOfToken(loc);
}
TransformActions::RewriteReceiver::~RewriteReceiver() { }
TransformActions::TransformActions(DiagnosticsEngine &diag,
CapturedDiagList &capturedDiags,
ASTContext &ctx, Preprocessor &PP)
: Diags(diag), CapturedDiags(capturedDiags) {
Impl = new TransformActionsImpl(capturedDiags, ctx, PP);
}
TransformActions::~TransformActions() {
delete static_cast<TransformActionsImpl*>(Impl);
}
void TransformActions::startTransaction() {
static_cast<TransformActionsImpl*>(Impl)->startTransaction();
}
bool TransformActions::commitTransaction() {
return static_cast<TransformActionsImpl*>(Impl)->commitTransaction();
}
void TransformActions::abortTransaction() {
static_cast<TransformActionsImpl*>(Impl)->abortTransaction();
}
void TransformActions::insert(SourceLocation loc, StringRef text) {
static_cast<TransformActionsImpl*>(Impl)->insert(loc, text);
}
void TransformActions::insertAfterToken(SourceLocation loc,
StringRef text) {
static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text);
}
void TransformActions::remove(SourceRange range) {
static_cast<TransformActionsImpl*>(Impl)->remove(range);
}
void TransformActions::removeStmt(Stmt *S) {
static_cast<TransformActionsImpl*>(Impl)->removeStmt(S);
}
void TransformActions::replace(SourceRange range, StringRef text) {
static_cast<TransformActionsImpl*>(Impl)->replace(range, text);
}
void TransformActions::replace(SourceRange range,
SourceRange replacementRange) {
static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange);
}
void TransformActions::replaceStmt(Stmt *S, StringRef text) {
static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text);
}
void TransformActions::replaceText(SourceLocation loc, StringRef text,
StringRef replacementText) {
static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text,
replacementText);
}
void TransformActions::increaseIndentation(SourceRange range,
SourceLocation parentIndent) {
static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range,
parentIndent);
}
bool TransformActions::clearDiagnostic(ArrayRef<unsigned> IDs,
SourceRange range) {
return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range);
}
void TransformActions::applyRewrites(RewriteReceiver &receiver) {
static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver);
}
DiagnosticBuilder TransformActions::report(SourceLocation loc, unsigned diagId,
SourceRange range) {
assert(!static_cast<TransformActionsImpl *>(Impl)->isInTransaction() &&
"Errors should be emitted out of a transaction");
return Diags.Report(loc, diagId) << range;
}
void TransformActions::reportError(StringRef message, SourceLocation loc,
SourceRange range) {
report(loc, diag::err_mt_message, range) << message;
}
void TransformActions::reportWarning(StringRef message, SourceLocation loc,
SourceRange range) {
report(loc, diag::warn_mt_message, range) << message;
}
void TransformActions::reportNote(StringRef message, SourceLocation loc,
SourceRange range) {
report(loc, diag::note_mt_message, range) << message;
}

@ -1,594 +0,0 @@
//===--- Transforms.cpp - Transformations to ARC mode ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/ARCMigrate/ARCMT.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaObjC.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
ASTTraverser::~ASTTraverser() { }
bool MigrationPass::CFBridgingFunctionsDefined() {
if (!EnableCFBridgeFns)
EnableCFBridgeFns = SemaRef.ObjC().isKnownName("CFBridgingRetain") &&
SemaRef.ObjC().isKnownName("CFBridgingRelease");
return *EnableCFBridgeFns;
}
//===----------------------------------------------------------------------===//
// Helpers.
//===----------------------------------------------------------------------===//
bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
bool AllowOnUnknownClass) {
if (!Ctx.getLangOpts().ObjCWeakRuntime)
return false;
QualType T = type;
if (T.isNull())
return false;
// iOS is always safe to use 'weak'.
if (Ctx.getTargetInfo().getTriple().isiOS() ||
Ctx.getTargetInfo().getTriple().isWatchOS())
AllowOnUnknownClass = true;
while (const PointerType *ptr = T->getAs<PointerType>())
T = ptr->getPointeeType();
if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) {
ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl();
if (!AllowOnUnknownClass && (!Class || Class->getName() == "NSObject"))
return false; // id/NSObject is not safe for weak.
if (!AllowOnUnknownClass && !Class->hasDefinition())
return false; // forward classes are not verifiable, therefore not safe.
if (Class && Class->isArcWeakrefUnavailable())
return false;
}
return true;
}
bool trans::isPlusOneAssign(const BinaryOperator *E) {
if (E->getOpcode() != BO_Assign)
return false;
return isPlusOne(E->getRHS());
}
bool trans::isPlusOne(const Expr *E) {
if (!E)
return false;
if (const FullExpr *FE = dyn_cast<FullExpr>(E))
E = FE->getSubExpr();
if (const ObjCMessageExpr *
ME = dyn_cast<ObjCMessageExpr>(E->IgnoreParenCasts()))
if (ME->getMethodFamily() == OMF_retain)
return true;
if (const CallExpr *
callE = dyn_cast<CallExpr>(E->IgnoreParenCasts())) {
if (const FunctionDecl *FD = callE->getDirectCallee()) {
if (FD->hasAttr<CFReturnsRetainedAttr>())
return true;
if (FD->isGlobal() &&
FD->getIdentifier() &&
FD->getParent()->isTranslationUnit() &&
FD->isExternallyVisible() &&
ento::cocoa::isRefType(callE->getType(), "CF",
FD->getIdentifier()->getName())) {
StringRef fname = FD->getIdentifier()->getName();
if (fname.ends_with("Retain") || fname.contains("Create") ||
fname.contains("Copy"))
return true;
}
}
}
const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E);
while (implCE && implCE->getCastKind() == CK_BitCast)
implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
return implCE && implCE->getCastKind() == CK_ARCConsumeObject;
}
/// 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
/// source location will be invalid.
SourceLocation trans::findLocationAfterSemi(SourceLocation loc,
ASTContext &Ctx, bool IsDecl) {
SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx, IsDecl);
if (SemiLoc.isInvalid())
return SourceLocation();
return SemiLoc.getLocWithOffset(1);
}
/// \arg Loc is the end of a statement range. This returns the location
/// of the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
/// source location will be invalid.
SourceLocation trans::findSemiAfterLocation(SourceLocation loc,
ASTContext &Ctx,
bool IsDecl) {
SourceManager &SM = Ctx.getSourceManager();
if (loc.isMacroID()) {
if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc))
return SourceLocation();
}
loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOpts());
// Break down the source location.
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
// Try to load the file buffer.
bool invalidTemp = false;
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
if (invalidTemp)
return SourceLocation();
const char *tokenBegin = file.data() + locInfo.second;
// Lex from the start of the given location.
Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
Ctx.getLangOpts(),
file.begin(), tokenBegin, file.end());
Token tok;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::semi)) {
if (!IsDecl)
return SourceLocation();
// Declaration may be followed with other tokens; such as an __attribute,
// before ending with a semicolon.
return findSemiAfterLocation(tok.getLocation(), Ctx, /*IsDecl*/true);
}
return tok.getLocation();
}
bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) {
if (!E || !E->HasSideEffects(Ctx))
return false;
E = E->IgnoreParenCasts();
ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
if (!ME)
return true;
switch (ME->getMethodFamily()) {
case OMF_autorelease:
case OMF_dealloc:
case OMF_release:
case OMF_retain:
switch (ME->getReceiverKind()) {
case ObjCMessageExpr::SuperInstance:
return false;
case ObjCMessageExpr::Instance:
return hasSideEffects(ME->getInstanceReceiver(), Ctx);
default:
break;
}
break;
default:
break;
}
return true;
}
bool trans::isGlobalVar(Expr *E) {
E = E->IgnoreParenCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl()->getDeclContext()->isFileContext() &&
DRE->getDecl()->isExternallyVisible();
if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))
return isGlobalVar(condOp->getTrueExpr()) &&
isGlobalVar(condOp->getFalseExpr());
return false;
}
StringRef trans::getNilString(MigrationPass &Pass) {
return Pass.SemaRef.PP.isMacroDefined("nil") ? "nil" : "0";
}
namespace {
class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {
ExprSet &Refs;
public:
ReferenceClear(ExprSet &refs) : Refs(refs) { }
bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; }
};
class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {
ValueDecl *Dcl;
ExprSet &Refs;
public:
ReferenceCollector(ValueDecl *D, ExprSet &refs)
: Dcl(D), Refs(refs) { }
bool VisitDeclRefExpr(DeclRefExpr *E) {
if (E->getDecl() == Dcl)
Refs.insert(E);
return true;
}
};
class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {
ExprSet &Removables;
public:
RemovablesCollector(ExprSet &removables)
: Removables(removables) { }
bool shouldWalkTypesOfTypeLocs() const { return false; }
bool TraverseStmtExpr(StmtExpr *E) {
CompoundStmt *S = E->getSubStmt();
for (CompoundStmt::body_iterator
I = S->body_begin(), E = S->body_end(); I != E; ++I) {
if (I != E - 1)
mark(*I);
TraverseStmt(*I);
}
return true;
}
bool VisitCompoundStmt(CompoundStmt *S) {
for (auto *I : S->body())
mark(I);
return true;
}
bool VisitIfStmt(IfStmt *S) {
mark(S->getThen());
mark(S->getElse());
return true;
}
bool VisitWhileStmt(WhileStmt *S) {
mark(S->getBody());
return true;
}
bool VisitDoStmt(DoStmt *S) {
mark(S->getBody());
return true;
}
bool VisitForStmt(ForStmt *S) {
mark(S->getInit());
mark(S->getInc());
mark(S->getBody());
return true;
}
private:
void mark(Stmt *S) {
if (!S) return;
while (auto *Label = dyn_cast<LabelStmt>(S))
S = Label->getSubStmt();
if (auto *E = dyn_cast<Expr>(S))
S = E->IgnoreImplicit();
if (auto *E = dyn_cast<Expr>(S))
Removables.insert(E);
}
};
} // end anonymous namespace
void trans::clearRefsIn(Stmt *S, ExprSet &refs) {
ReferenceClear(refs).TraverseStmt(S);
}
void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {
ReferenceCollector(D, refs).TraverseStmt(S);
}
void trans::collectRemovables(Stmt *S, ExprSet &exprs) {
RemovablesCollector(exprs).TraverseStmt(S);
}
//===----------------------------------------------------------------------===//
// MigrationContext
//===----------------------------------------------------------------------===//
namespace {
class ASTTransform : public RecursiveASTVisitor<ASTTransform> {
MigrationContext &MigrateCtx;
typedef RecursiveASTVisitor<ASTTransform> base;
public:
ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { }
bool shouldWalkTypesOfTypeLocs() const { return false; }
bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
ObjCImplementationContext ImplCtx(MigrateCtx, D);
for (MigrationContext::traverser_iterator
I = MigrateCtx.traversers_begin(),
E = MigrateCtx.traversers_end(); I != E; ++I)
(*I)->traverseObjCImplementation(ImplCtx);
return base::TraverseObjCImplementationDecl(D);
}
bool TraverseStmt(Stmt *rootS) {
if (!rootS)
return true;
BodyContext BodyCtx(MigrateCtx, rootS);
for (MigrationContext::traverser_iterator
I = MigrateCtx.traversers_begin(),
E = MigrateCtx.traversers_end(); I != E; ++I)
(*I)->traverseBody(BodyCtx);
return true;
}
};
}
MigrationContext::~MigrationContext() {
for (traverser_iterator
I = traversers_begin(), E = traversers_end(); I != E; ++I)
delete *I;
}
bool MigrationContext::isGCOwnedNonObjC(QualType T) {
while (!T.isNull()) {
if (const AttributedType *AttrT = T->getAs<AttributedType>()) {
if (AttrT->getAttrKind() == attr::ObjCOwnership)
return !AttrT->getModifiedType()->isObjCRetainableType();
}
if (T->isArrayType())
T = Pass.Ctx.getBaseElementType(T);
else if (const PointerType *PT = T->getAs<PointerType>())
T = PT->getPointeeType();
else if (const ReferenceType *RT = T->getAs<ReferenceType>())
T = RT->getPointeeType();
else
break;
}
return false;
}
bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr,
StringRef toAttr,
SourceLocation atLoc) {
if (atLoc.isMacroID())
return false;
SourceManager &SM = Pass.Ctx.getSourceManager();
// Break down the source location.
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
// Try to load the file buffer.
bool invalidTemp = false;
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
if (invalidTemp)
return false;
const char *tokenBegin = file.data() + locInfo.second;
// Lex from the start of the given location.
Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
Pass.Ctx.getLangOpts(),
file.begin(), tokenBegin, file.end());
Token tok;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::at)) return false;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::raw_identifier)) return false;
if (tok.getRawIdentifier() != "property")
return false;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::l_paren)) return false;
Token BeforeTok = tok;
Token AfterTok;
AfterTok.startToken();
SourceLocation AttrLoc;
lexer.LexFromRawLexer(tok);
if (tok.is(tok::r_paren))
return false;
while (true) {
if (tok.isNot(tok::raw_identifier)) return false;
if (tok.getRawIdentifier() == fromAttr) {
if (!toAttr.empty()) {
Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
return true;
}
// We want to remove the attribute.
AttrLoc = tok.getLocation();
}
do {
lexer.LexFromRawLexer(tok);
if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
AfterTok = tok;
} while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
if (tok.is(tok::r_paren))
break;
if (AttrLoc.isInvalid())
BeforeTok = tok;
lexer.LexFromRawLexer(tok);
}
if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
// We want to remove the attribute.
if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
AfterTok.getLocation()));
} else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
} else {
Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
}
return true;
}
return false;
}
bool MigrationContext::addPropertyAttribute(StringRef attr,
SourceLocation atLoc) {
if (atLoc.isMacroID())
return false;
SourceManager &SM = Pass.Ctx.getSourceManager();
// Break down the source location.
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
// Try to load the file buffer.
bool invalidTemp = false;
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
if (invalidTemp)
return false;
const char *tokenBegin = file.data() + locInfo.second;
// Lex from the start of the given location.
Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
Pass.Ctx.getLangOpts(),
file.begin(), tokenBegin, file.end());
Token tok;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::at)) return false;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::raw_identifier)) return false;
if (tok.getRawIdentifier() != "property")
return false;
lexer.LexFromRawLexer(tok);
if (tok.isNot(tok::l_paren)) {
Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
return true;
}
lexer.LexFromRawLexer(tok);
if (tok.is(tok::r_paren)) {
Pass.TA.insert(tok.getLocation(), attr);
return true;
}
if (tok.isNot(tok::raw_identifier)) return false;
Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
return true;
}
void MigrationContext::traverse(TranslationUnitDecl *TU) {
for (traverser_iterator
I = traversers_begin(), E = traversers_end(); I != E; ++I)
(*I)->traverseTU(*this);
ASTTransform(*this).TraverseDecl(TU);
}
static void GCRewriteFinalize(MigrationPass &pass) {
ASTContext &Ctx = pass.Ctx;
TransformActions &TA = pass.TA;
DeclContext *DC = Ctx.getTranslationUnitDecl();
Selector FinalizeSel =
Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
impl_iterator;
for (impl_iterator I = impl_iterator(DC->decls_begin()),
E = impl_iterator(DC->decls_end()); I != E; ++I) {
for (const auto *MD : I->instance_methods()) {
if (!MD->hasBody())
continue;
if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
const ObjCMethodDecl *FinalizeM = MD;
Transaction Trans(TA);
TA.insert(FinalizeM->getSourceRange().getBegin(),
"#if !__has_feature(objc_arc)\n");
CharSourceRange::getTokenRange(FinalizeM->getSourceRange());
const SourceManager &SM = pass.Ctx.getSourceManager();
const LangOptions &LangOpts = pass.Ctx.getLangOpts();
bool Invalid;
std::string str = "\n#endif\n";
str += Lexer::getSourceText(
CharSourceRange::getTokenRange(FinalizeM->getSourceRange()),
SM, LangOpts, &Invalid);
TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str);
break;
}
}
}
}
//===----------------------------------------------------------------------===//
// getAllTransformations.
//===----------------------------------------------------------------------===//
static void traverseAST(MigrationPass &pass) {
MigrationContext MigrateCtx(pass);
if (pass.isGCMigration()) {
MigrateCtx.addTraverser(new GCCollectableCallsTraverser);
MigrateCtx.addTraverser(new GCAttrsTraverser());
}
MigrateCtx.addTraverser(new PropertyRewriteTraverser());
MigrateCtx.addTraverser(new BlockObjCVariableTraverser());
MigrateCtx.addTraverser(new ProtectedScopeTraverser());
MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());
}
static void independentTransforms(MigrationPass &pass) {
rewriteAutoreleasePool(pass);
removeRetainReleaseDeallocFinalize(pass);
rewriteUnusedInitDelegate(pass);
removeZeroOutPropsInDeallocFinalize(pass);
makeAssignARCSafe(pass);
rewriteUnbridgedCasts(pass);
checkAPIUses(pass);
traverseAST(pass);
}
std::vector<TransformFn> arcmt::getAllTransformations(
LangOptions::GCMode OrigGCMode,
bool NoFinalizeRemoval) {
std::vector<TransformFn> transforms;
if (OrigGCMode == LangOptions::GCOnly && NoFinalizeRemoval)
transforms.push_back(GCRewriteFinalize);
transforms.push_back(independentTransforms);
// This depends on previous transformations removing various expressions.
transforms.push_back(removeEmptyStatementsAndDeallocFinalize);
return transforms;
}

@ -1,224 +0,0 @@
//===-- Transforms.h - Transformations to ARC mode --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H
#define LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H
#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/SaveAndRestore.h"
namespace clang {
class Decl;
class Stmt;
class BlockDecl;
class ObjCMethodDecl;
class FunctionDecl;
namespace arcmt {
class MigrationPass;
namespace trans {
class MigrationContext;
//===----------------------------------------------------------------------===//
// Transformations.
//===----------------------------------------------------------------------===//
void rewriteAutoreleasePool(MigrationPass &pass);
void rewriteUnbridgedCasts(MigrationPass &pass);
void makeAssignARCSafe(MigrationPass &pass);
void removeRetainReleaseDeallocFinalize(MigrationPass &pass);
void removeZeroOutPropsInDeallocFinalize(MigrationPass &pass);
void rewriteUnusedInitDelegate(MigrationPass &pass);
void checkAPIUses(MigrationPass &pass);
void removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass);
class BodyContext {
MigrationContext &MigrateCtx;
ParentMap PMap;
Stmt *TopStmt;
public:
BodyContext(MigrationContext &MigrateCtx, Stmt *S)
: MigrateCtx(MigrateCtx), PMap(S), TopStmt(S) {}
MigrationContext &getMigrationContext() { return MigrateCtx; }
ParentMap &getParentMap() { return PMap; }
Stmt *getTopStmt() { return TopStmt; }
};
class ObjCImplementationContext {
MigrationContext &MigrateCtx;
ObjCImplementationDecl *ImpD;
public:
ObjCImplementationContext(MigrationContext &MigrateCtx,
ObjCImplementationDecl *D)
: MigrateCtx(MigrateCtx), ImpD(D) {}
MigrationContext &getMigrationContext() { return MigrateCtx; }
ObjCImplementationDecl *getImplementationDecl() { return ImpD; }
};
class ASTTraverser {
public:
virtual ~ASTTraverser();
virtual void traverseTU(MigrationContext &MigrateCtx) { }
virtual void traverseBody(BodyContext &BodyCtx) { }
virtual void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) {}
};
class MigrationContext {
std::vector<ASTTraverser *> Traversers;
public:
MigrationPass &Pass;
struct GCAttrOccurrence {
enum AttrKind { Weak, Strong } Kind;
SourceLocation Loc;
QualType ModifiedType;
Decl *Dcl;
/// true if the attribute is owned, e.g. it is in a body and not just
/// in an interface.
bool FullyMigratable;
};
std::vector<GCAttrOccurrence> GCAttrs;
llvm::DenseSet<SourceLocation> AttrSet;
llvm::DenseSet<SourceLocation> RemovedAttrSet;
/// Set of raw '@' locations for 'assign' properties group that contain
/// GC __weak.
llvm::DenseSet<SourceLocation> AtPropsWeak;
explicit MigrationContext(MigrationPass &pass) : Pass(pass) {}
~MigrationContext();
typedef std::vector<ASTTraverser *>::iterator traverser_iterator;
traverser_iterator traversers_begin() { return Traversers.begin(); }
traverser_iterator traversers_end() { return Traversers.end(); }
void addTraverser(ASTTraverser *traverser) {
Traversers.push_back(traverser);
}
bool isGCOwnedNonObjC(QualType T);
bool removePropertyAttribute(StringRef fromAttr, SourceLocation atLoc) {
return rewritePropertyAttribute(fromAttr, StringRef(), atLoc);
}
bool rewritePropertyAttribute(StringRef fromAttr, StringRef toAttr,
SourceLocation atLoc);
bool addPropertyAttribute(StringRef attr, SourceLocation atLoc);
void traverse(TranslationUnitDecl *TU);
void dumpGCAttrs();
};
class PropertyRewriteTraverser : public ASTTraverser {
public:
void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) override;
};
class BlockObjCVariableTraverser : public ASTTraverser {
public:
void traverseBody(BodyContext &BodyCtx) override;
};
class ProtectedScopeTraverser : public ASTTraverser {
public:
void traverseBody(BodyContext &BodyCtx) override;
};
// GC transformations
class GCAttrsTraverser : public ASTTraverser {
public:
void traverseTU(MigrationContext &MigrateCtx) override;
};
class GCCollectableCallsTraverser : public ASTTraverser {
public:
void traverseBody(BodyContext &BodyCtx) override;
};
//===----------------------------------------------------------------------===//
// Helpers.
//===----------------------------------------------------------------------===//
/// Determine whether we can add weak to the given type.
bool canApplyWeak(ASTContext &Ctx, QualType type,
bool AllowOnUnknownClass = false);
bool isPlusOneAssign(const BinaryOperator *E);
bool isPlusOne(const Expr *E);
/// 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
/// source location will be invalid.
SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx,
bool IsDecl = false);
/// 'Loc' is the end of a statement range. This returns the location
/// of the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
/// source location will be invalid.
SourceLocation findSemiAfterLocation(SourceLocation loc, ASTContext &Ctx,
bool IsDecl = false);
bool hasSideEffects(Expr *E, ASTContext &Ctx);
bool isGlobalVar(Expr *E);
/// Returns "nil" or "0" if 'nil' macro is not actually defined.
StringRef getNilString(MigrationPass &Pass);
template <typename BODY_TRANS>
class BodyTransform : public RecursiveASTVisitor<BodyTransform<BODY_TRANS> > {
MigrationPass &Pass;
Decl *ParentD;
typedef RecursiveASTVisitor<BodyTransform<BODY_TRANS> > base;
public:
BodyTransform(MigrationPass &pass) : Pass(pass), ParentD(nullptr) { }
bool TraverseStmt(Stmt *rootS) {
if (rootS)
BODY_TRANS(Pass).transformBody(rootS, ParentD);
return true;
}
bool TraverseObjCMethodDecl(ObjCMethodDecl *D) {
SaveAndRestore<Decl *> SetParent(ParentD, D);
return base::TraverseObjCMethodDecl(D);
}
};
typedef llvm::DenseSet<Expr *> ExprSet;
void clearRefsIn(Stmt *S, ExprSet &refs);
template <typename iterator>
void clearRefsIn(iterator begin, iterator end, ExprSet &refs) {
for (; begin != end; ++begin)
clearRefsIn(*begin, refs);
}
void collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs);
void collectRemovables(Stmt *S, ExprSet &exprs);
} // end namespace trans
} // end namespace arcmt
} // end namespace clang
#endif

@ -12,9 +12,6 @@ add_subdirectory(Analysis)
add_subdirectory(Edit)
add_subdirectory(ExtractAPI)
add_subdirectory(Rewrite)
if(CLANG_ENABLE_ARCMT)
add_subdirectory(ARCMigrate)
endif()
add_subdirectory(Driver)
add_subdirectory(Serialization)
add_subdirectory(Frontend)

@ -27,8 +27,8 @@ const char *Action::getClassName(ActionClass AC) {
case PrecompileJobClass: return "precompiler";
case ExtractAPIJobClass:
return "api-extractor";
case AnalyzeJobClass: return "analyzer";
case MigrateJobClass: return "migrator";
case AnalyzeJobClass:
return "analyzer";
case CompileJobClass: return "compiler";
case BackendJobClass: return "backend";
case AssembleJobClass: return "assembler";
@ -373,11 +373,6 @@ void AnalyzeJobAction::anchor() {}
AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType)
: JobAction(AnalyzeJobClass, Input, OutputType) {}
void MigrateJobAction::anchor() {}
MigrateJobAction::MigrateJobAction(Action *Input, types::ID OutputType)
: JobAction(MigrateJobClass, Input, OutputType) {}
void CompileJobAction::anchor() {}
CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType)

@ -413,12 +413,12 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
// -{fsyntax-only,-analyze,emit-ast} only run up to the compiler.
} else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) ||
(PhaseArg = DAL.getLastArg(options::OPT_print_supported_cpus)) ||
(PhaseArg = DAL.getLastArg(options::OPT_print_enabled_extensions)) ||
(PhaseArg =
DAL.getLastArg(options::OPT_print_enabled_extensions)) ||
(PhaseArg = DAL.getLastArg(options::OPT_module_file_info)) ||
(PhaseArg = DAL.getLastArg(options::OPT_verify_pch)) ||
(PhaseArg = DAL.getLastArg(options::OPT_rewrite_objc)) ||
(PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) ||
(PhaseArg = DAL.getLastArg(options::OPT__migrate)) ||
(PhaseArg = DAL.getLastArg(options::OPT__analyze)) ||
(PhaseArg = DAL.getLastArg(options::OPT_emit_cir)) ||
(PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) {
@ -5082,8 +5082,6 @@ Action *Driver::ConstructPhaseAction(
types::TY_RewrittenLegacyObjC);
if (Args.hasArg(options::OPT__analyze))
return C.MakeAction<AnalyzeJobAction>(Input, types::TY_Plist);
if (Args.hasArg(options::OPT__migrate))
return C.MakeAction<MigrateJobAction>(Input, types::TY_Remap);
if (Args.hasArg(options::OPT_emit_ast))
return C.MakeAction<CompileJobAction>(Input, types::TY_AST);
if (Args.hasArg(options::OPT_emit_cir))

@ -646,7 +646,6 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
case Action::PreprocessJobClass:
case Action::ExtractAPIJobClass:
case Action::AnalyzeJobClass:
case Action::MigrateJobClass:
case Action::VerifyPCHJobClass:
case Action::BackendJobClass:
return getClang();

@ -3958,78 +3958,6 @@ static void RenderOpenACCOptions(const Driver &D, const ArgList &Args,
}
}
static void RenderARCMigrateToolOptions(const Driver &D, const ArgList &Args,
ArgStringList &CmdArgs) {
bool ARCMTEnabled = false;
if (!Args.hasArg(options::OPT_fno_objc_arc, options::OPT_fobjc_arc)) {
if (const Arg *A = Args.getLastArg(options::OPT_ccc_arcmt_check,
options::OPT_ccc_arcmt_modify,
options::OPT_ccc_arcmt_migrate)) {
ARCMTEnabled = true;
switch (A->getOption().getID()) {
default: llvm_unreachable("missed a case");
case options::OPT_ccc_arcmt_check:
CmdArgs.push_back("-arcmt-action=check");
break;
case options::OPT_ccc_arcmt_modify:
CmdArgs.push_back("-arcmt-action=modify");
break;
case options::OPT_ccc_arcmt_migrate:
CmdArgs.push_back("-arcmt-action=migrate");
CmdArgs.push_back("-mt-migrate-directory");
CmdArgs.push_back(A->getValue());
Args.AddLastArg(CmdArgs, options::OPT_arcmt_migrate_report_output);
Args.AddLastArg(CmdArgs, options::OPT_arcmt_migrate_emit_arc_errors);
break;
}
}
} else {
Args.ClaimAllArgs(options::OPT_ccc_arcmt_check);
Args.ClaimAllArgs(options::OPT_ccc_arcmt_modify);
Args.ClaimAllArgs(options::OPT_ccc_arcmt_migrate);
}
if (const Arg *A = Args.getLastArg(options::OPT_ccc_objcmt_migrate)) {
if (ARCMTEnabled)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< A->getAsString(Args) << "-ccc-arcmt-migrate";
CmdArgs.push_back("-mt-migrate-directory");
CmdArgs.push_back(A->getValue());
if (!Args.hasArg(options::OPT_objcmt_migrate_literals,
options::OPT_objcmt_migrate_subscripting,
options::OPT_objcmt_migrate_property)) {
// None specified, means enable them all.
CmdArgs.push_back("-objcmt-migrate-literals");
CmdArgs.push_back("-objcmt-migrate-subscripting");
CmdArgs.push_back("-objcmt-migrate-property");
} else {
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_literals);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_subscripting);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_property);
}
} else {
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_literals);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_subscripting);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_property);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_all);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_readonly_property);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_readwrite_property);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_property_dot_syntax);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_annotation);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_instancetype);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_nsmacros);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_protocol_conformance);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_atomic_property);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_returns_innerpointer_property);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_ns_nonatomic_iosonly);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_migrate_designated_init);
Args.AddLastArg(CmdArgs, options::OPT_objcmt_allowlist_dir_path);
}
}
static void RenderBuiltinOptions(const ToolChain &TC, const llvm::Triple &T,
const ArgList &Args, ArgStringList &CmdArgs) {
// -fbuiltin is default unless -mkernel is used.
@ -5319,8 +5247,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (isa<AnalyzeJobAction>(JA)) {
assert(JA.getType() == types::TY_Plist && "Invalid output type.");
CmdArgs.push_back("-analyze");
} else if (isa<MigrateJobAction>(JA)) {
CmdArgs.push_back("-migrate");
} else if (isa<PreprocessJobAction>(JA)) {
if (Output.getType() == types::TY_Dependencies)
CmdArgs.push_back("-Eonly");
@ -6445,8 +6371,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_working_directory);
RenderARCMigrateToolOptions(D, Args, CmdArgs);
// Add preprocessing options like -I, -D, etc. if we are using the
// preprocessor.
//

@ -588,20 +588,6 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA,
// more information.
ArgStringList CmdArgs;
/// Hack(tm) to ignore linking errors when we are doing ARC migration.
if (Args.hasArg(options::OPT_ccc_arcmt_check,
options::OPT_ccc_arcmt_migrate)) {
for (const auto &Arg : Args)
Arg->claim();
const char *Exec =
Args.MakeArgString(getToolChain().GetProgramPath("touch"));
CmdArgs.push_back(Output.getFilename());
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::None(), Exec,
CmdArgs, std::nullopt, Output));
return;
}
VersionTuple Version = getMachOToolChain().getLinkerVersion(Args);
bool LinkerIsLLD;

@ -2756,7 +2756,6 @@ static const auto &getFrontendActionTable() {
{frontend::RewriteObjC, OPT_rewrite_objc},
{frontend::RewriteTest, OPT_rewrite_test},
{frontend::RunAnalysis, OPT_analyze},
{frontend::MigrateSource, OPT_migrate},
{frontend::RunPreprocessorOnly, OPT_Eonly},
{frontend::PrintDependencyDirectivesSourceMinimizerOutput,
OPT_print_dependency_directives_minimized_source},
@ -3099,12 +3098,6 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
if (Args.hasArg(OPT_aux_target_feature))
Opts.AuxTargetFeatures = Args.getAllArgValues(OPT_aux_target_feature);
if (Opts.ARCMTAction != FrontendOptions::ARCMT_None &&
Opts.ObjCMTAction != FrontendOptions::ObjCMT_None) {
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< "ARC migration" << "ObjC migration";
}
InputKind DashX(Language::Unknown);
if (const Arg *A = Args.getLastArg(OPT_x)) {
StringRef XValue = A->getValue();
@ -4639,7 +4632,6 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
case frontend::RewriteTest:
case frontend::RunAnalysis:
case frontend::TemplightDump:
case frontend::MigrateSource:
return false;
case frontend::DumpCompilerOptions:

@ -21,12 +21,6 @@ if(CLANG_ENABLE_CIR)
)
endif()
if(CLANG_ENABLE_ARCMT)
list(APPEND link_libs
clangARCMigrate
)
endif()
if(CLANG_ENABLE_STATIC_ANALYZER)
list(APPEND link_libs
clangStaticAnalyzerFrontend

@ -11,7 +11,6 @@
//
//===----------------------------------------------------------------------===//
#include "clang/ARCMigrate/ARCMTActions.h"
#include "clang/CodeGen/CodeGenAction.h"
#include "clang/Config/config.h"
#include "clang/Driver/Options.h"
@ -131,12 +130,6 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
#else
case RewriteObjC: Action = "RewriteObjC"; break;
#endif
#if CLANG_ENABLE_ARCMT
case MigrateSource:
return std::make_unique<arcmt::MigrateSourceAction>();
#else
case MigrateSource: Action = "MigrateSource"; break;
#endif
#if CLANG_ENABLE_STATIC_ANALYZER
case RunAnalysis: return std::make_unique<ento::AnalysisAction>();
#else
@ -147,8 +140,7 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
return std::make_unique<PrintDependencyDirectivesSourceMinimizerAction>();
}
#if !CLANG_ENABLE_ARCMT || !CLANG_ENABLE_STATIC_ANALYZER \
|| !CLANG_ENABLE_OBJC_REWRITER
#if !CLANG_ENABLE_STATIC_ANALYZER || !CLANG_ENABLE_OBJC_REWRITER
CI.getDiagnostics().Report(diag::err_fe_action_not_available) << Action;
return 0;
#else
@ -169,35 +161,6 @@ CreateFrontendAction(CompilerInstance &CI) {
Act = std::make_unique<FixItRecompile>(std::move(Act));
}
#if CLANG_ENABLE_ARCMT
if (CI.getFrontendOpts().ProgramAction != frontend::MigrateSource &&
CI.getFrontendOpts().ProgramAction != frontend::GeneratePCH) {
// Potentially wrap the base FE action in an ARC Migrate Tool action.
switch (FEOpts.ARCMTAction) {
case FrontendOptions::ARCMT_None:
break;
case FrontendOptions::ARCMT_Check:
Act = std::make_unique<arcmt::CheckAction>(std::move(Act));
break;
case FrontendOptions::ARCMT_Modify:
Act = std::make_unique<arcmt::ModifyAction>(std::move(Act));
break;
case FrontendOptions::ARCMT_Migrate:
Act = std::make_unique<arcmt::MigrateAction>(std::move(Act),
FEOpts.MTMigrateDir,
FEOpts.ARCMTMigrateReportOut,
FEOpts.ARCMTMigrateEmitARCErrors);
break;
}
if (FEOpts.ObjCMTAction != FrontendOptions::ObjCMT_None) {
Act = std::make_unique<arcmt::ObjCMigrateAction>(std::move(Act),
FEOpts.MTMigrateDir,
FEOpts.ObjCMTAction);
}
}
#endif
// Wrap the base FE action in an extract api action to generate
// symbol graph as a biproduct of compilation (enabled with
// --emit-symbol-graph option)

@ -214,9 +214,6 @@ makeCommonInvocationForModuleBuild(CompilerInvocation CI) {
CI.getDependencyOutputOpts().Targets.clear();
CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
CI.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None;
CI.getFrontendOpts().ObjCMTAction = FrontendOptions::ObjCMT_None;
CI.getFrontendOpts().MTMigrateDir.clear();
CI.getLangOpts().ModuleName.clear();
// Remove any macro definitions that are explicitly ignored.

@ -1,110 +0,0 @@
#if __has_feature(objc_arr)
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
#else
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
#endif
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#define CF_CONSUMED __attribute__((cf_consumed))
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
#define NS_INLINE static __inline__ __attribute__((always_inline))
#define nil ((void*) 0)
#define NULL ((void*)0)
typedef int BOOL;
typedef unsigned NSUInteger;
typedef int int32_t;
typedef unsigned char uint8_t;
typedef int32_t UChar32;
typedef unsigned char UChar;
typedef struct _NSZone NSZone;
typedef const void * CFTypeRef;
CFTypeRef CFRetain(CFTypeRef cf);
CFTypeRef CFMakeCollectable(CFTypeRef cf) NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
NS_INLINE NS_RETURNS_RETAINED id NSMakeCollectable(CFTypeRef CF_CONSUMED cf) NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
@protocol NSObject
- (BOOL)isEqual:(id)object;
- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
@end
@interface NSObject <NSObject> {}
- (id)init;
+ (id)new;
+ (id)alloc;
- (void)dealloc;
- (void)finalize;
- (id)copy;
- (id)mutableCopy;
@end
NS_AUTOMATED_REFCOUNT_UNAVAILABLE
@interface NSAutoreleasePool : NSObject {
@private
void *_token;
void *_reserved3;
void *_reserved2;
void *_reserved;
}
+ (void)addObject:(id)anObject;
- (void)addObject:(id)anObject;
- (void)drain;
@end
typedef const void* objc_objectptr_t;
extern __attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer);
extern __attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer);
extern objc_objectptr_t objc_unretainedPointer(id object);
#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
typedef id dispatch_object_t;
typedef id xpc_object_t;
void _dispatch_object_validate(dispatch_object_t object);
void _xpc_object_validate(xpc_object_t object);
#if __has_feature(objc_arc)
NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
return (__bridge_retained CFTypeRef)X;
}
NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
return (__bridge_transfer id)X;
}
#else
NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
return X ? CFRetain((CFTypeRef)X) : NULL;
}
NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
return [(id)CFMakeCollectable(X) autorelease];
}
#endif
void *_Block_copy(const void *aBlock);
void _Block_release(const void *aBlock);
#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))

@ -1,10 +0,0 @@
// RUN: %clang_cc1 -arcmt-action=check -no-ns-alloc-error -triple x86_64-apple-darwin10 -fobjc-gc-only %s 2>&1 | grep 'warning: \[rewriter\] call returns pointer to GC managed memory'
// RUN: %clang_cc1 -arcmt-action=check -no-ns-alloc-error -triple x86_64-apple-darwin10 -fobjc-gc-only -x objective-c++ %s 2>&1 | grep 'warning: \[rewriter\] call returns pointer to GC managed memory'
// TODO: Investigate VerifyDiagnosticConsumer failures on these tests when using -verify.
typedef unsigned NSUInteger;
void *__strong NSAllocateCollectable(NSUInteger size, NSUInteger options);
void test1(void) {
NSAllocateCollectable(100, 0);
}

@ -1,19 +0,0 @@
// RUN: %clang_cc1 -arcmt-action=check -verify -triple x86_64-apple-darwin10 -fobjc-gc-only %s
// RUN: %clang_cc1 -arcmt-action=check -verify -triple x86_64-apple-darwin10 -fobjc-gc-only -x objective-c++ %s
#define CF_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
typedef unsigned NSUInteger;
typedef const void * CFTypeRef;
CFTypeRef CFMakeCollectable(CFTypeRef cf) CF_AUTOMATED_REFCOUNT_UNAVAILABLE; // expected-note {{unavailable}}
void *__strong NSAllocateCollectable(NSUInteger size, NSUInteger options);
void test1(CFTypeRef *cft) {
CFTypeRef c = CFMakeCollectable(cft); // expected-error {{CFMakeCollectable will leak the object that it receives in ARC}} \
// expected-error {{unavailable}}
NSAllocateCollectable(100, 0); // expected-error {{call returns pointer to GC managed memory; it will become unmanaged in ARC}}
}
@interface I1 {
__strong void *gcVar; // expected-error {{GC managed memory will become unmanaged in ARC}}
}
@end;

@ -1,80 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.6 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fsyntax-only -fobjc-gc-only -x objective-c %s > %t
// RUN: diff %t %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fsyntax-only -fobjc-gc-only -x objective-c++ %s > %t
// RUN: diff %t %s.result
// MRC __weak broke this test somehow.
// XFAIL: *
#include "Common.h"
#include "GC.h"
void test1(CFTypeRef *cft) {
id x = NSMakeCollectable(cft);
}
@interface I1
@end
@implementation I1
-(void)dealloc {
// dealloc
test1(0);
}
-(void)finalize {
// finalize
test1(0);
}
@end
@interface I2
@property (retain) id prop;
@end
@implementation I2
@synthesize prop;
-(void)finalize {
self.prop = 0;
// finalize
test1(0);
}
@end
__attribute__((objc_arc_weak_reference_unavailable))
@interface QQ {
__weak id s;
__weak QQ *q;
}
@end
@interface I3
@property (assign) I3 *__weak pw1, *__weak pw2;
@property (assign) I3 *__strong ps;
@property (assign) I3 * pds;
@end
@interface I4Impl {
I4Impl *pds2;
}
@property (assign) I4Impl *__weak pw1, *__weak pw2;
@property (assign) I4Impl *__strong ps;
@property (assign) I4Impl * pds;
@property (assign) I4Impl * pds2;
@end
@implementation I4Impl
@synthesize pw1, pw2, ps, pds, pds2;
-(void)test1:(CFTypeRef *)cft {
id x = NSMakeCollectable(cft);
}
@end
@interface I5 {
__weak id prop;
}
@property (readonly) __weak id prop;
@end

@ -1,72 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.6 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fsyntax-only -fobjc-gc-only -x objective-c %s > %t
// RUN: diff %t %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fsyntax-only -fobjc-gc-only -x objective-c++ %s > %t
// RUN: diff %t %s.result
#include "Common.h"
#include "GC.h"
void test1(CFTypeRef *cft) {
id x = CFBridgingRelease(cft);
}
@interface I1
@end
@implementation I1
-(void)dealloc {
// dealloc
test1(0);
}
@end
@interface I2
@property (strong) id prop;
@end
@implementation I2
@synthesize prop;
-(void)dealloc {
// finalize
test1(0);
}
@end
__attribute__((objc_arc_weak_reference_unavailable))
@interface QQ {
__unsafe_unretained id s;
__unsafe_unretained QQ *q;
}
@end
@interface I3
@property (unsafe_unretained) I3 * pw1, * pw2;
@property (strong) I3 * ps;
@property (assign) I3 * pds;
@end
@interface I4Impl {
I4Impl *__strong pds2;
}
@property (unsafe_unretained) I4Impl * pw1, * pw2;
@property (strong) I4Impl * ps;
@property (strong) I4Impl * pds;
@property (strong) I4Impl * pds2;
@end
@implementation I4Impl
@synthesize pw1, pw2, ps, pds, pds2;
-(void)test1:(CFTypeRef *)cft {
id x = CFBridgingRelease(cft);
}
@end
@interface I5 {
__unsafe_unretained id prop;
}
@property (unsafe_unretained, readonly) id prop;
@end

@ -1,88 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -no-finalize-removal -x objective-c %s > %t
// RUN: diff %t %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -no-finalize-removal -x objective-c++ %s > %t
// RUN: diff %t %s.result
#include "Common.h"
#include "GC.h"
void test1(CFTypeRef *cft) {
id x = NSMakeCollectable(cft);
}
@interface I1
@end
@implementation I1
-(void)dealloc {
// dealloc
test1(0);
}
-(void)finalize {
// finalize
test1(0);
}
@end
@interface I2
@property (retain) id prop;
@end
@implementation I2
@synthesize prop;
-(void)finalize {
self.prop = 0;
// finalize
test1(0);
}
@end
__attribute__((objc_arc_weak_reference_unavailable))
@interface QQ {
__weak id s;
__weak QQ *q;
}
@end
@interface I3
@property (assign) I3 *__weak pw1, *__weak pw2;
@property (assign) I3 *__strong ps;
@property (assign) I3 * pds;
@end
@interface I4Impl {
I4Impl *pds2;
I4Impl *pds3;
__weak I4Impl *pw3;
__weak I4Impl *pw4;
}
@property (assign) I4Impl *__weak pw1, *__weak pw2;
@property (assign) I4Impl *__strong ps;
@property (assign) I4Impl * pds;
@property (assign) I4Impl * pds2;
@property (readwrite) I4Impl * pds3;
@property (readonly) I4Impl * pds4;
@property (readonly) __weak I4Impl *pw3;
@property (assign) __weak I4Impl *pw4;
@end
@implementation I4Impl
@synthesize pw1, pw2, pw3, pw4, ps, pds, pds2, pds3, pds4;
-(void)test1:(CFTypeRef *)cft {
id x = NSMakeCollectable(cft);
}
@end
@interface rdar10532449
@property (assign) id assign_prop;
@property (assign, readonly) id __strong strong_readonly_prop;
@property (assign) id __weak weak_prop;
@end
@implementation rdar10532449
@synthesize assign_prop, strong_readonly_prop, weak_prop;
@end

@ -1,96 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -no-finalize-removal -x objective-c %s > %t
// RUN: diff %t %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -no-finalize-removal -x objective-c++ %s > %t
// RUN: diff %t %s.result
#include "Common.h"
#include "GC.h"
void test1(CFTypeRef *cft) {
id x = CFBridgingRelease(cft);
}
@interface I1
@end
@implementation I1
-(void)dealloc {
// dealloc
test1(0);
}
#if !__has_feature(objc_arc)
-(void)finalize {
// finalize
test1(0);
}
#endif
@end
@interface I2
@property (strong) id prop;
@end
@implementation I2
@synthesize prop;
#if !__has_feature(objc_arc)
-(void)finalize {
self.prop = 0;
// finalize
test1(0);
}
#endif
-(void)dealloc {
// finalize
test1(0);
}
@end
__attribute__((objc_arc_weak_reference_unavailable))
@interface QQ {
__weak id s;
__unsafe_unretained QQ *q;
}
@end
@interface I3
@property (weak) I3 * pw1, * pw2;
@property (strong) I3 * ps;
@property (assign) I3 * pds;
@end
@interface I4Impl {
I4Impl *__strong pds2;
I4Impl *pds3;
__weak I4Impl *pw3;
__weak I4Impl *pw4;
}
@property (weak) I4Impl * pw1, * pw2;
@property (strong) I4Impl * ps;
@property (strong) I4Impl * pds;
@property (strong) I4Impl * pds2;
@property (readwrite) I4Impl * pds3;
@property (readonly) I4Impl * pds4;
@property (weak, readonly) I4Impl *pw3;
@property (weak) I4Impl *pw4;
@end
@implementation I4Impl
@synthesize pw1, pw2, pw3, pw4, ps, pds, pds2, pds3, pds4;
-(void)test1:(CFTypeRef *)cft {
id x = CFBridgingRelease(cft);
}
@end
@interface rdar10532449
@property (strong) id assign_prop;
@property (strong, readonly) id strong_readonly_prop;
@property (weak) id weak_prop;
@end
@implementation rdar10532449
@synthesize assign_prop, strong_readonly_prop, weak_prop;
@end

@ -1,6 +0,0 @@
@interface ExtInterface {
__strong ExtInterface *myivar;
__strong void *gcVar;
}
@end

@ -1,93 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -x objective-c %s > %t
// RUN: diff %t %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -x objective-c++ %s > %t
// RUN: diff %t %s.result
#include "Common.h"
#include "GC.h"
void test1(CFTypeRef *cft) {
id x = NSMakeCollectable(cft);
}
@interface I1
@end
@implementation I1
-(void)dealloc {
// dealloc
test1(0);
}
-(void)finalize {
// finalize
test1(0);
}
@end
@interface I2
@property (retain) id prop;
@end
@implementation I2
@synthesize prop;
-(void)finalize {
self.prop = 0;
// finalize
test1(0);
}
@end
__attribute__((objc_arc_weak_reference_unavailable))
@interface QQ {
__weak id s;
__weak QQ *q;
}
@end
@interface I3
@property (assign) I3 *__weak pw1, *__weak pw2;
@property (assign) I3 *__strong ps;
@property (assign) I3 * pds;
@end
@interface I4Impl {
I4Impl *pds2;
I4Impl *pds3;
__weak I4Impl *pw3;
__weak I4Impl *pw4;
}
@property (assign) I4Impl *__weak pw1, *__weak pw2;
@property (assign) I4Impl *__strong ps;
@property (assign) I4Impl * pds;
@property (assign) I4Impl * pds2;
@property (readwrite) I4Impl * pds3;
@property (readonly) I4Impl * pds4;
@property (readonly) __weak I4Impl *pw3;
@property (assign) __weak I4Impl *pw4;
@end
@implementation I4Impl
@synthesize pw1, pw2, pw3, pw4, ps, pds, pds2, pds3, pds4;
-(void)test1:(CFTypeRef *)cft {
id x = NSMakeCollectable(cft);
}
@end
@interface rdar10532449
@property (assign) id assign_prop;
@property (assign, readonly) id __strong strong_readonly_prop;
@property (assign) id __weak weak_prop;
@end
@implementation rdar10532449
@synthesize assign_prop, strong_readonly_prop, weak_prop;
@end
void test2(id p, __strong I1 *ap[]) {
for (__strong I1 *specRule in p) {
}
}

@ -1,88 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -x objective-c %s > %t
// RUN: diff %t %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only -fobjc-gc-only -x objective-c++ %s > %t
// RUN: diff %t %s.result
#include "Common.h"
#include "GC.h"
void test1(CFTypeRef *cft) {
id x = CFBridgingRelease(cft);
}
@interface I1
@end
@implementation I1
-(void)dealloc {
// dealloc
test1(0);
}
@end
@interface I2
@property (strong) id prop;
@end
@implementation I2
@synthesize prop;
-(void)dealloc {
// finalize
test1(0);
}
@end
__attribute__((objc_arc_weak_reference_unavailable))
@interface QQ {
__weak id s;
__unsafe_unretained QQ *q;
}
@end
@interface I3
@property (weak) I3 * pw1, * pw2;
@property (strong) I3 * ps;
@property (assign) I3 * pds;
@end
@interface I4Impl {
I4Impl *__strong pds2;
I4Impl *pds3;
__weak I4Impl *pw3;
__weak I4Impl *pw4;
}
@property (weak) I4Impl * pw1, * pw2;
@property (strong) I4Impl * ps;
@property (strong) I4Impl * pds;
@property (strong) I4Impl * pds2;
@property (readwrite) I4Impl * pds3;
@property (readonly) I4Impl * pds4;
@property (weak, readonly) I4Impl *pw3;
@property (weak) I4Impl *pw4;
@end
@implementation I4Impl
@synthesize pw1, pw2, pw3, pw4, ps, pds, pds2, pds3, pds4;
-(void)test1:(CFTypeRef *)cft {
id x = CFBridgingRelease(cft);
}
@end
@interface rdar10532449
@property (strong) id assign_prop;
@property (strong, readonly) id strong_readonly_prop;
@property (weak) id weak_prop;
@end
@implementation rdar10532449
@synthesize assign_prop, strong_readonly_prop, weak_prop;
@end
void test2(id p, __strong I1 *ap[]) {
for (__strong I1 *specRule in p) {
}
}

@ -1,5 +0,0 @@
#ifndef MODULE_SUBFRAMEWORK_H
#define MODULE_SUBFRAMEWORK_H
#__private_macro MODULE_SUBFRAMEWORK_H
char *module_subframework;
#endif

@ -1 +0,0 @@
unsigned *Buried_Treasure;

@ -1,28 +0,0 @@
// expected-warning 0-1 {{umbrella header}}
// FIXME: The "umbrella header" warning should be moved to a separate test.
// This "0-1" is only here because the warning is only emitted when the
// module is (otherwise) successfully included.
#ifndef MODULE_H
#define MODULE_H
const char *getModuleVersion(void);
#ifdef FOO
# error Module should have been built without -DFOO
#endif
@interface Module
+(const char *)version; // retrieve module version
+alloc;
@end
#define MODULE_H_MACRO 1
#__private_macro MODULE_H_MACRO
#include <Module/Sub.h>
#include <Module/Buried/Treasure.h>
__asm("foo");
#endif // MODULE_H

@ -1,3 +0,0 @@
#include <Module/Sub2.h>
int *Module_Sub;

@ -1,309 +0,0 @@
module c_library [extern_c] { module inner { header "c-header.h" } }
module cxx_library { header "cxx-header.h" requires cplusplus }
module c_library_bad [extern_c] { header "c-header-bad.h" }
module diamond_top { header "diamond_top.h" }
module diamond_left {
header "diamond_left.h"
export diamond_top
}
module diamond_right {
header "diamond_right.h"
export diamond_top
}
module diamond_bottom {
header "diamond_bottom.h"
export *
}
module irgen { header "irgen.h" }
module cxx_irgen_top { header "cxx-irgen-top.h" }
module cxx_irgen_left { header "cxx-irgen-left.h" }
module cxx_irgen_right { header "cxx-irgen-right.h" }
module lookup_left_objc { header "lookup_left.h" }
module lookup_right_objc { header "lookup_right.h" }
module lookup_left_cxx { header "lookup_left.hpp" }
module lookup_right_cxx { header "lookup_right.hpp" }
module module_private_left { header "module_private_left.h" }
module module_private_right { header "module_private_right.h" }
module macros_top {
header "macros_top.h"
explicit module b { header "macros_top_b.h" }
explicit module c { header "macros_top_c.h" }
}
module macros_left {
header "macros_left.h"
export *
}
module macros_right {
header "macros_right.h"
export *
explicit module undef {
header "macros_right_undef.h"
}
}
module macros { header "macros.h" }
module macros_other { header "macros_other.h" }
module category_top { header "category_top.h" }
module category_left {
header "category_left.h"
export category_top
explicit module sub {
header "category_left_sub.h"
}
}
module category_right {
header "category_right.h"
export category_top
explicit module sub {
header "category_right_sub.h"
}
}
module category_bottom {
header "category_bottom.h"
export category_left
export category_right
}
module category_other { header "category_other.h" }
module redeclarations_left { header "redeclarations_left.h" }
module redeclarations_right { header "redeclarations_right.h" }
module redecl_namespaces_left { header "redecl_namespaces_left.h" }
module redecl_namespaces_right { header "redecl_namespaces_right.h" }
module redecl_add_after_load_top { header "redecl-add-after-load-top.h" }
module redecl_add_after_load { header "redecl-add-after-load.h" }
module load_failure { header "load_failure.h" }
module decldef {
explicit module Decl { header "decl.h" }
explicit module Decl2 { header "decl2.h" }
explicit module Def { header "def.h" }
}
module redecl_merge_top {
header "redecl-merge-top.h"
explicit module Explicit { header "redecl-merge-top-explicit.h" }
exclude header "nonexistent.h"
}
module redecl_merge_left {
header "redecl-merge-left.h"
export *
}
module redecl_merge_left_left {
header "redecl-merge-left-left.h"
export *
}
module redecl_merge_right {
header "redecl-merge-right.h"
export *
}
module redecl_merge_bottom {
explicit module prefix {
header "redecl-merge-bottom-prefix.h"
}
header "redecl-merge-bottom.h"
export *
}
module namespaces_top {
header "namespaces-top.h"
export *
}
module namespaces_left {
header "namespaces-left.h"
export *
}
module namespaces_right {
header "namespaces-right.h"
export *
}
module templates_top {
header "templates-top.h"
export *
}
module templates_left {
header "templates-left.h"
export *
}
module templates_right {
header "templates-right.h"
export *
}
module MethodPoolA {
header "MethodPoolA.h"
explicit module Sub2 {
header "MethodPoolASub2.h"
}
explicit module Sub {
header "MethodPoolASub.h"
}
}
module MethodPoolB {
header "MethodPoolB.h"
explicit module Sub2 {
header "MethodPoolBSub2.h"
}
explicit module Sub {
header "MethodPoolBSub.h"
}
}
module import_decl {
header "import-decl.h"
}
framework module * {
exclude NotAModule
}
module linkage_merge_left {
explicit module sub {
header "linkage-merge-sub.h"
}
}
module autolink {
header "autolink.h"
link "autolink"
explicit module sub {
header "autolink-sub.h"
link "autolink_sub"
}
explicit module sub2 {
header "autolink-sub2.h"
link framework "autolink_framework"
}
explicit module sub3 {
header "autolink-sub3.h"
link "autolink_from_pch"
}
}
module weird_objc {
header "weird_objc.h"
}
module ignored_macros {
header "ignored_macros.h"
}
module cxx_many_overloads {
header "cxx-many-overloads.h"
}
module cxx_inline_namespace {
header "cxx-inline-namespace.h"
}
module cxx_inline_namespace_b {
header "cxx-inline-namespace-b.h"
}
module cxx_linkage_cache {
header "cxx-linkage-cache.h"
}
module cxx_templates_common {
header "cxx-templates-common.h"
}
module cxx_templates_a {
header "cxx-templates-a.h"
}
module cxx_templates_b_impl {
header "cxx-templates-b-impl.h"
}
module cxx_templates_b {
header "cxx-templates-b.h"
}
module cxx_templates_c {
header "cxx-templates-c.h"
}
module cxx_decls {
module unimported {
header "cxx-decls-unimported.h"
}
module imported {
header "cxx-decls-imported.h"
}
}
module config {
header "config.h"
config_macros [exhaustive] WANT_FOO, WANT_BAR
}
module diag_pragma {
header "diag_pragma.h"
}
module dummy {
header "dummy.h"
}
module builtin {
header "builtin.h"
explicit module sub {
header "builtin_sub.h"
}
}
module linkage_merge {
explicit module foo {
header "linkage-merge-foo.h"
}
explicit module bar {
header "linkage-merge-bar.h"
}
}
module incomplete_mod {
header "incomplete_mod.h"
}
module warning {
header "warning.h"
}
module initializer_list {
header "initializer_list"
}
module using_decl {
module a { header "using-decl-a.h" export * }
module b { header "using-decl-b.h" export * }
}
module recursive_visibility_a1 {
module inner { header "recursive_visibility_a1_inner.h" }
}
module recursive_visibility_a2 {
module inner {
module more_inner {
header "recursive_visibility_a2_more_inner.h"
}
}
}
module recursive_visibility_b {
header "recursive_visibility_b.h"
export *
}
module recursive_visibility_c {
header "recursive_visibility_c.h"
}
module recursive1 {
header "recursive1.h"
}
module recursive2 {
header "recursive2.h"
}

@ -1,15 +0,0 @@
@protocol NSObject
- (oneway void)release;
@end
#ifdef PART1
static inline void part1(id p) {
[p release];
}
#endif
#ifdef PART2
static inline void part2(id p) {
[p release];
}
#endif

@ -1,13 +0,0 @@
@protocol NSObject
- (oneway void)release;
@end
#ifdef PART1
static inline void part1(id p) {
}
#endif
#ifdef PART2
static inline void part2(id p) {
}
#endif

@ -1,16 +0,0 @@
#define PART1
#include "test.h"
void test1(id p) {
[p release];
}
@interface Test2
@property (strong) id prop;
@end
@implementation Test2
-(id)init {
_prop = 0;
}
@end

@ -1,15 +0,0 @@
#define PART1
#include "test.h"
void test1(id p) {
}
@interface Test2
@property (strong) id prop;
@end
@implementation Test2
-(id)init {
_prop = 0;
}
@end

@ -1,6 +0,0 @@
#define PART2
#include "test.h"
void test2(id p) {
[p release];
}

@ -1,5 +0,0 @@
#define PART2
#include "test.h"
void test2(id p) {
}

@ -1,15 +0,0 @@
@protocol NSObject
- (oneway void)release;
@end
#ifdef PART1
static inline void part1(id p) {
[p release];
}
#endif
#ifdef PART2
static inline void part2(id p) {
[p release];
}
#endif

@ -1,13 +0,0 @@
@protocol NSObject
- (oneway void)release;
@end
#ifdef PART1
static inline void part1(id p) {
}
#endif
#ifdef PART2
static inline void part2(id p) {
}
#endif

@ -1,6 +0,0 @@
#define PART1
#include "test.h"
void test1(id p) {
[p release];
}

@ -1,5 +0,0 @@
#define PART1
#include "test.h"
void test1(id p) {
}

@ -1,6 +0,0 @@
#define PART2
#include "test.h"
void test2(id p) {
[p release];
}

@ -1,5 +0,0 @@
#define PART2
#include "test.h"
void test2(id p) {
}

@ -1 +0,0 @@
// the contents are not important

@ -1,8 +0,0 @@
@interface I1 : NSObject
-(int)prop;
-(void)setProp:(int)p;
+(id)i1;
@end
typedef long NSInteger;

@ -1,7 +0,0 @@
@interface I1 : NSObject
@property (nonatomic) int prop;
+(instancetype)i1;
@end
typedef long NSInteger;

@ -1,8 +0,0 @@
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
typedef enum : NSInteger {five} ApplicableEnum;
@interface I2 : NSObject
-(int)prop;
-(void)setProp:(int)p;
@end

@ -1,7 +0,0 @@
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
typedef NS_ENUM(NSInteger, ApplicableEnum) {five};
@interface I2 : NSObject
@property (nonatomic) int prop;
@end

@ -1,18 +0,0 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-readwrite-property -objcmt-migrate-instancetype -objcmt-allowlist-dir-path=%S/Inputs %s -triple x86_64-apple-darwin11 -migrate -o %t.remap
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %S/header1.h.result %s.result
@interface NSObject
+ (id)alloc;
@end
#include "header1.h"
#include "header2.h"
@interface I2(cat)
-(id)initInCat;
@end
@implementation I1
+(id)i1 {}
@end

@ -1,18 +0,0 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-readwrite-property -objcmt-migrate-instancetype -objcmt-allowlist-dir-path=%S/Inputs %s -triple x86_64-apple-darwin11 -migrate -o %t.remap
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %S/header1.h.result %s.result
@interface NSObject
+ (id)alloc;
@end
#include "header1.h"
#include "header2.h"
@interface I2(cat)
-(id)initInCat;
@end
@implementation I1
+(instancetype)i1 {}
@end

@ -1,12 +0,0 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -objcmt-migrate-readwrite-property -objcmt-migrate-instancetype -objcmt-migrate-ns-macros %s -triple x86_64-apple-darwin11 -migrate -o %t.remap
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %S/header1.h.result %S/header2.h.result
// RUN: %clang_cc1 -objcmt-migrate-readwrite-property -objcmt-migrate-instancetype -objcmt-migrate-ns-macros -objcmt-allowlist-dir-path=%S/Inputs %s -triple x86_64-apple-darwin11 -migrate -o %t.remap
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %S/header1.h.result
@interface NSObject
+ (id)alloc;
@end
#include "header1.h"
#include "header2.h"

@ -1,9 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
#include "Common.h"
void test(NSObject *o) {
NSZone *z = [o zone];
}

@ -1,9 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
#include "Common.h"
void test(NSObject *o) {
NSZone *z = nil;
}

@ -1,15 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fsyntax-only %s > %t
// RUN: diff %t %s.result
#include "Common.h"
@interface Foo : NSObject {
NSObject *x;
}
@property (readonly,assign) id x;
@end
@implementation Foo
@synthesize x;
@end

@ -1,15 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fsyntax-only %s > %t
// RUN: diff %t %s.result
#include "Common.h"
@interface Foo : NSObject {
NSObject *__unsafe_unretained x;
}
@property (readonly,unsafe_unretained) id x;
@end
@implementation Foo
@synthesize x;
@end

@ -1,72 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only %s > %t
// RUN: diff %t %s.result
#include "Common.h"
__attribute__((objc_arc_weak_reference_unavailable))
@interface WeakOptOut
@end
@class _NSCachedAttributedString;
typedef _NSCachedAttributedString *BadClassForWeak;
@class Forw;
@interface Foo : NSObject {
Foo *x, *w, *q1, *q2;
WeakOptOut *oo;
BadClassForWeak bcw;
id not_safe1;
NSObject *not_safe2;
Forw *not_safe3;
Foo *assign_plus1;
}
@property (readonly) Foo *x;
@property (assign) Foo *w;
@property Foo *q1, *q2;
@property (assign) WeakOptOut *oo;
@property (assign) BadClassForWeak bcw;
@property (assign) id not_safe1;
@property () NSObject *not_safe2;
@property Forw *not_safe3;
@property (readonly) Foo *assign_plus1;
@property (readonly) Foo *assign_plus2;
@property (readonly) Foo *assign_plus3;
@property (assign) Foo *no_user_ivar1;
@property (readonly) Foo *no_user_ivar2;
@property (retain) id def1;
@property (atomic,retain) id def2;
@property (retain,atomic) id def3;
@end
@implementation Foo
@synthesize x,w,q1,q2,oo,bcw,not_safe1,not_safe2,not_safe3;
@synthesize no_user_ivar1, no_user_ivar2;
@synthesize assign_plus1, assign_plus2, assign_plus3;
@synthesize def1, def2, def3;
-(void)test:(Foo *)parm {
assign_plus1 = [[Foo alloc] init];
assign_plus2 = [Foo new];
assign_plus3 = [parm retain];
}
@end
@interface TestExt
@property (retain,readonly) TestExt *x1;
@property (readonly) TestExt *x2;
@end
@interface TestExt()
@property (retain,readwrite) TestExt *x1;
@property (readwrite) TestExt *x2;
@property (retain) TestExt *x3;
@end
@implementation TestExt
@synthesize x1, x2, x3;
@end

@ -1,72 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fsyntax-only %s > %t
// RUN: diff %t %s.result
#include "Common.h"
__attribute__((objc_arc_weak_reference_unavailable))
@interface WeakOptOut
@end
@class _NSCachedAttributedString;
typedef _NSCachedAttributedString *BadClassForWeak;
@class Forw;
@interface Foo : NSObject {
Foo *__weak x, *__weak w, *__weak q1, *__weak q2;
WeakOptOut *__unsafe_unretained oo;
BadClassForWeak __unsafe_unretained bcw;
id __unsafe_unretained not_safe1;
NSObject *__unsafe_unretained not_safe2;
Forw *__unsafe_unretained not_safe3;
Foo *assign_plus1;
}
@property (weak, readonly) Foo *x;
@property (weak) Foo *w;
@property (weak) Foo *q1, *q2;
@property (unsafe_unretained) WeakOptOut *oo;
@property (unsafe_unretained) BadClassForWeak bcw;
@property (unsafe_unretained) id not_safe1;
@property (unsafe_unretained) NSObject *not_safe2;
@property (unsafe_unretained) Forw *not_safe3;
@property (readonly) Foo *assign_plus1;
@property (readonly) Foo *assign_plus2;
@property (readonly) Foo *assign_plus3;
@property (weak) Foo *no_user_ivar1;
@property (weak, readonly) Foo *no_user_ivar2;
@property (strong) id def1;
@property (atomic,strong) id def2;
@property (strong,atomic) id def3;
@end
@implementation Foo
@synthesize x,w,q1,q2,oo,bcw,not_safe1,not_safe2,not_safe3;
@synthesize no_user_ivar1, no_user_ivar2;
@synthesize assign_plus1, assign_plus2, assign_plus3;
@synthesize def1, def2, def3;
-(void)test:(Foo *)parm {
assign_plus1 = [[Foo alloc] init];
assign_plus2 = [Foo new];
assign_plus3 = parm;
}
@end
@interface TestExt
@property (strong,readonly) TestExt *x1;
@property (weak, readonly) TestExt *x2;
@end
@interface TestExt()
@property (strong,readwrite) TestExt *x1;
@property (weak, readwrite) TestExt *x2;
@property (strong) TestExt *x3;
@end
@implementation TestExt
@synthesize x1, x2, x3;
@end

@ -1,29 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
@interface NSAutoreleasePool
- drain;
+new;
+alloc;
-init;
-autorelease;
-release;
@end
void NSLog(id, ...);
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
while (argc) {
[chunkPool release];
return 0;
}
[chunkPool drain];
[pool drain];
return 0;
}

@ -1,28 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
@interface NSAutoreleasePool
- drain;
+new;
+alloc;
-init;
-autorelease;
-release;
@end
void NSLog(id, ...);
int main (int argc, const char * argv[]) {
@autoreleasepool {
@autoreleasepool {
while (argc) {
return 0;
}
}
}
return 0;
}

@ -1,40 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
@interface NSAutoreleasePool
- drain;
+new;
+alloc;
-init;
-autorelease;
- release;
@end
void NSLog(id, ...);
void test1(int x) {
// All this stuff get removed since nothing is happening inside.
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
while (x) {
chunkPool = [[NSAutoreleasePool alloc] init];
[chunkPool release];
}
[chunkPool drain];
[pool drain];
}
void test2(int x) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
while (x) {
chunkPool = [[NSAutoreleasePool alloc] init];
++x;
[chunkPool release];
}
[chunkPool drain];
[pool drain];
}

@ -1,31 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
@interface NSAutoreleasePool
- drain;
+new;
+alloc;
-init;
-autorelease;
- release;
@end
void NSLog(id, ...);
void test1(int x) {
// All this stuff get removed since nothing is happening inside.
}
void test2(int x) {
@autoreleasepool {
@autoreleasepool {
while (x) {
@autoreleasepool {
++x;
}
}
}
}
}

@ -1,144 +0,0 @@
// RUN: %clang_cc1 -arcmt-action=check -verify -triple x86_64-apple-darwin10 %s
#if __has_feature(objc_arr)
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
#else
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
#endif
typedef struct _NSZone NSZone;
typedef int BOOL;
typedef unsigned NSUInteger;
@protocol NSObject
- (BOOL)isEqual:(id)object;
- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
@end
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
@interface NSObject <NSObject> {}
- (id)init;
+ (id)new;
+ (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (id)alloc;
- (void)dealloc;
- (void)finalize;
- (id)copy;
- (id)mutableCopy;
+ (id)copyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (id)mutableCopyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
@end
extern void NSRecycleZone(NSZone *zone);
NS_AUTOMATED_REFCOUNT_UNAVAILABLE
@interface NSAutoreleasePool : NSObject { // expected-note 13 {{marked unavailable here}}
@private
void *_token;
void *_reserved3;
void *_reserved2;
void *_reserved;
}
+ (void)addObject:(id)anObject;
- (void)addObject:(id)anObject;
- (void)drain;
@end
void NSLog(id, ...);
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}}
while (argc) {
[chunkPool release];
// the following pool was not released in this scope, don't touch it.
chunkPool = [[NSAutoreleasePool alloc] init]; // expected-error {{'NSAutoreleasePool' is unavailable}}
}
[chunkPool drain];
[pool drain];
return 0;
}
void f(void) {
NSAutoreleasePool * pool; // expected-error {{'NSAutoreleasePool' is unavailable}}
for (int i=0; i != 10; ++i) {
id x = pool; // We won't touch a NSAutoreleasePool if we can't safely
// remove all the references to it.
}
pool = [[NSAutoreleasePool alloc] init]; // expected-error {{'NSAutoreleasePool' is unavailable}}
NSLog(@"%s", "YES");
[pool release];
}
void f2(void) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
// expected-note {{scope begins here}}
// 'x' is declared inside the "pool scope" but used outside it, if we create
// a @autorelease scope it will be undefined outside it so don't touch the pool.
int x = 0; // expected-note {{declared here}}
[pool release]; // expected-note {{scope ends here}}
++x; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
}
void f3(void) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
// expected-note {{scope begins here}}
struct S { int x; }; // expected-note {{declared here}}
[pool release]; // expected-note {{scope ends here}}
struct S *var; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
var->x = 0;
}
void f4(void) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
// expected-note {{scope begins here}}
enum { Bar }; // expected-note {{declared here}}
[pool release]; // expected-note {{scope ends here}}
int x = Bar; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
}
void f5(void) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \
// expected-note {{scope begins here}}
typedef int Bar; // expected-note {{declared here}}
[pool release]; // expected-note {{scope ends here}}
Bar x; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}}
}

@ -1,61 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
#include "Common.h"
void NSLog(id, ...);
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (argc) {
NSAutoreleasePool * pool = [NSAutoreleasePool new];
NSLog(@"%s", "YES");
[pool drain];
}
[pool drain];
NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init];
NSLog(@"%s", "YES");
[pool1 release];
return 0;
}
void f(void) {
NSAutoreleasePool *pool1;
pool1 = [NSAutoreleasePool new];
int x = 4;
NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
++x;
[pool2 drain];
[pool1 release];
}
int UIApplicationMain(int argc, char *argv[]);
int main2(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int result = UIApplicationMain(argc, argv);
[pool release];
return result;
}
@interface Foo : NSObject
@property (assign) id myProp;
@end
@implementation Foo
@synthesize myProp;
-(void)test:(id)p {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool drain];
self.myProp = p;
}
@end

@ -1,60 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
#include "Common.h"
void NSLog(id, ...);
int main (int argc, const char * argv[]) {
@autoreleasepool {
if (argc) {
@autoreleasepool {
NSLog(@"%s", "YES");
}
}
}
@autoreleasepool {
NSLog(@"%s", "YES");
}
return 0;
}
void f(void) {
@autoreleasepool {
int x = 4;
@autoreleasepool {
++x;
}
}
}
int UIApplicationMain(int argc, char *argv[]);
int main2(int argc, char *argv[]) {
@autoreleasepool {
int result = UIApplicationMain(argc, argv);
return result;
}
}
@interface Foo : NSObject
@property (unsafe_unretained) id myProp;
@end
@implementation Foo
@synthesize myProp;
-(void)test:(id)p {
@autoreleasepool {
}
self.myProp = p;
}
@end

@ -1,75 +0,0 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
#include "Common.h"
@interface A : NSObject {
@package
id object;
}
@end
@interface B : NSObject {
id _prop;
xpc_object_t _xpc_prop;
}
- (BOOL)containsSelf:(A*)a;
@property (retain) id prop;
@property (retain) xpc_object_t xpc_prop;
@end
@implementation A
@end
@implementation B
- (BOOL)containsSelf:(A*)a {
return a->object == self;
}
-(id) prop {
return _prop;
}
-(void) setProp:(id) newVal {
[_prop autorelease];
_prop = [newVal retain];
}
-(void) setProp2:(CFTypeRef) newVal {
[_prop autorelease];
_prop = (id)CFRetain(newVal);
}
-(id) xpc_prop {
return _xpc_prop;
}
-(void) setXpc_prop:(xpc_object_t) newVal {
[_xpc_prop autorelease];
_xpc_prop = xpc_retain(newVal);
}
@end
void NSLog(id, ...);
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
A *a = [[A new] autorelease];
B *b = [[B new] autorelease];
NSLog(@"%s", [b containsSelf:a] ? "YES" : "NO");
[pool drain];
return 0;
}
void test(A *prevVal, A *newVal) {
[prevVal autorelease];
prevVal = [newVal retain];
}
id test2(A* val) {
[[val retain] autorelease];
return val;
}
void test3(void) {
id a = [[A alloc] init];
[a autorelease];
}

Some files were not shown because too many files have changed in this diff Show More