mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-21 11:46:49 +00:00
[Clang] Remove ARCMigrate (#119269)
In the discussion around #116792, @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:
parent
11026a8d8b
commit
c4a019747c
clang
CMakeLists.txt
cmake/caches
docs
include
clang-c
clang
ARCMigrate
Basic
Config
Driver
Frontend
lib
ARCMigrate
ARCMT.cppARCMTActions.cppCMakeLists.txtFileRemapper.cppInternals.hObjCMT.cppPlistReporter.cppTransAPIUses.cppTransARCAssign.cppTransAutoreleasePool.cppTransBlockObjCVariable.cppTransEmptyStatementsAndDealloc.cppTransGCAttrs.cppTransGCCalls.cppTransProperties.cppTransProtectedScope.cppTransRetainReleaseDealloc.cppTransUnbridgedCasts.cppTransUnusedInitDelegate.cppTransZeroOutPropsInDealloc.cppTransformActions.cppTransforms.cppTransforms.h
CMakeLists.txtDriver
Frontend
FrontendTool
Tooling/DependencyScanning
test/ARCMT
Common.hGC-check-warn-nsalloc.mGC-check.mGC-no-arc-runtime.mGC-no-arc-runtime.m.resultGC-no-finalize-removal.mGC-no-finalize-removal.m.resultGC.hGC.mGC.m.resultapi.mapi.m.resultassign-prop-no-arc-runtime.massign-prop-no-arc-runtime.m.resultassign-prop-with-arc-runtime.massign-prop-with-arc-runtime.m.resultatautorelease-2.matautorelease-2.m.resultatautorelease-3.matautorelease-3.m.resultatautorelease-check.matautorelease.matautorelease.m.resultautoreleases.m
Inputs
Module.framework
module.modulemaptest.htest.h.resulttest1.m.intest1.m.in.resulttest2.m.intest2.m.in.resultwith space
allowlisted
Inputs
header1.hheader1.h.resultheader2.hheader2.h.resultobjcmt-with-allowlist-impl.mobjcmt-with-allowlist-impl.m.resultobjcmt-with-allowlist.m@ -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> ¯oLocs)
|
||||
: 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 +0,0 @@
|
||||
int not_in_module;
|
@ -1,3 +0,0 @@
|
||||
#include <Module/Sub2.h>
|
||||
int *Module_Sub;
|
||||
|
@ -1 +0,0 @@
|
||||
int *Module_Sub2;
|
@ -1 +0,0 @@
|
||||
int module_private;
|
@ -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
Loading…
x
Reference in New Issue
Block a user