14#include "clang/AST/DeclObjC.h"
15#include "clang/AST/PrettyPrinter.h"
16#include "clang/Basic/LLVM.h"
17#include "clang/Basic/LangOptions.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/Basic/SourceManager.h"
20#include "clang/Tooling/Core/Replacement.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/ADT/iterator_range.h"
23#include "llvm/Support/Casting.h"
24#include "llvm/Support/Error.h"
31static std::string capitalize(std::string Message) {
33 Message[0] = llvm::toUpper(Message[0]);
37static std::string getTypeStr(
const QualType &OrigT,
const Decl &D,
38 unsigned PropertyAttributes) {
40 PrintingPolicy Policy(D.getASTContext().getLangOpts());
41 Policy.SuppressStrongLifetime =
true;
45 if (PropertyAttributes & ObjCPropertyAttribute::kind_nullability) {
46 if (
auto Kind = AttributedType::stripOuterNullability(T)) {
48 case NullabilityKind::Nullable:
51 case NullabilityKind::NonNull:
54 case NullabilityKind::Unspecified:
55 Prefix =
"null_unspecified ";
57 case NullabilityKind::NullableResult:
63 return Prefix +
T.getAsString(Policy);
66struct MethodParameter {
76 MethodParameter(
const ObjCIvarDecl &
ID) {
80 Name.consume_front(
"_");
81 Type = getTypeStr(
ID.getType(),
ID, ObjCPropertyAttribute::kind_noattr);
84 MethodParameter(
const ObjCPropertyDecl &PD) {
86 Type = getTypeStr(PD.getType(), PD, PD.getPropertyAttributes());
87 if (
const auto *
ID = PD.getPropertyIvarDecl())
92 static std::optional<MethodParameter> parameterFor(
const Decl &D) {
93 if (
const auto *
ID = dyn_cast<ObjCIvarDecl>(&D))
94 return MethodParameter(*
ID);
95 if (
const auto *PD = dyn_cast<ObjCPropertyDecl>(&D))
96 if (PD->isInstanceProperty())
97 return MethodParameter(*PD);
102static SmallVector<MethodParameter, 8>
103getAllParams(
const ObjCInterfaceDecl *
ID) {
104 SmallVector<MethodParameter, 8> Params;
109 llvm::DenseSet<llvm::StringRef> Names;
110 for (
const auto *Ivar :
ID->ivars()) {
111 MethodParameter P(*Ivar);
112 if (Names.insert(P.Name).second)
115 for (
const auto *Prop :
ID->properties()) {
116 MethodParameter P(*Prop);
117 if (Names.insert(P.Name).second)
124initializerForParams(
const SmallVector<MethodParameter, 8> &Params,
127 llvm::raw_string_ostream Stream(Code);
129 if (Params.empty()) {
132 R
"cpp(- (instancetype)init {
140 Stream <<
"- (instancetype)init;";
143 const auto &First = Params.front();
144 Stream << llvm::formatv(
"- (instancetype)initWith{0}:({1}){2}",
145 capitalize(First.Name.trim().str()), First.Type,
147 for (
const auto &It : llvm::drop_begin(Params))
148 Stream << llvm::formatv(
" {0}:({1}){0}", It.Name, It.Type);
155 for (
const auto &Param : Params)
156 Stream << llvm::formatv(
"\n {0} = {1};", Param.Assignee, Param.Name);
172class ObjCMemberwiseInitializer :
public Tweak {
174 const char *id() const final;
175 llvm::StringLiteral kind()
const override {
179 bool prepare(
const Selection &Inputs)
override;
180 Expected<Tweak::Effect> apply(
const Selection &Inputs)
override;
181 std::string title()
const override;
184 SmallVector<MethodParameter, 8>
185 paramsForSelection(
const SelectionTree::Node *N);
187 const ObjCInterfaceDecl *Interface =
nullptr;
190 const ObjCImplementationDecl *Impl =
nullptr;
195bool ObjCMemberwiseInitializer::prepare(
const Selection &Inputs) {
196 const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
199 const Decl *D = N->ASTNode.get<
Decl>();
202 const auto &LangOpts = Inputs.AST->getLangOpts();
204 if (!LangOpts.ObjC || !LangOpts.ObjCAutoRefCount)
214 if (
const auto *
ID = dyn_cast<ObjCInterfaceDecl>(D)) {
216 if (!
ID->isThisDeclarationADefinition())
219 }
else if (
const auto *
ID = dyn_cast<ObjCImplementationDecl>(D)) {
222 }
else if (isa<ObjCPropertyDecl, ObjCIvarDecl>(D)) {
223 const auto *DC = D->getDeclContext();
224 if (
const auto *
ID = dyn_cast<ObjCInterfaceDecl>(DC)) {
226 }
else if (
const auto *
ID = dyn_cast<ObjCImplementationDecl>(DC)) {
234SmallVector<MethodParameter, 8>
235ObjCMemberwiseInitializer::paramsForSelection(
const SelectionTree::Node *N) {
236 SmallVector<MethodParameter, 8> Params;
238 if (
const auto *D = N->ASTNode.get<
Decl>()) {
239 if (
auto Param = MethodParameter::parameterFor(*D)) {
240 Params.push_back(*Param);
245 Impl ?
static_cast<const ObjCContainerDecl *
>(Impl)
246 :
static_cast<const ObjCContainerDecl *
>(
Interface);
247 if (
Container == N->ASTNode.get<ObjCContainerDecl>() && N->Children.empty())
250 llvm::DenseSet<llvm::StringRef> Names;
252 for (
const auto *CNode : N->Children) {
253 const Decl *D = CNode->ASTNode.get<
Decl>();
256 if (
auto P = MethodParameter::parameterFor(*D))
257 if (Names.insert(P->Name).second)
258 Params.push_back(*P);
263Expected<Tweak::Effect>
264ObjCMemberwiseInitializer::apply(
const Selection &Inputs) {
265 const auto &SM = Inputs.AST->getASTContext().getSourceManager();
266 const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
268 return error(
"Invalid selection");
270 SmallVector<MethodParameter, 8> Params = paramsForSelection(N);
273 std::vector<Anchor> Anchors = {
275 if (
const auto *MD = llvm::dyn_cast<ObjCMethodDecl>(D)) {
276 return MD->getMethodFamily() != OMF_init && MD->isInstanceMethod();
283 auto InterfaceReplacement =
284 insertDecl(initializerForParams(Params,
false),
286 if (!InterfaceReplacement)
287 return InterfaceReplacement.takeError();
288 auto FE = Effect::fileEdit(SM, SM.getFileID(
Interface->getLocation()),
289 tooling::Replacements(*InterfaceReplacement));
291 return FE.takeError();
292 E.ApplyEdits.insert(std::move(*FE));
298 initializerForParams(Params,
true), *Impl, Anchors);
299 if (!ImplReplacement)
300 return ImplReplacement.takeError();
302 if (SM.isWrittenInSameFile(
Interface->getLocation(), Impl->getLocation())) {
305 E.ApplyEdits.begin()->second.Replacements.add(*ImplReplacement))
306 return std::move(Err);
310 auto FE = Effect::fileEdit(SM, SM.getFileID(Impl->getLocation()),
311 tooling::Replacements(*ImplReplacement));
313 return FE.takeError();
314 E.ApplyEdits.insert(std::move(*FE));
320std::string ObjCMemberwiseInitializer::title()
const {
322 return "Generate memberwise initializer";
323 return "Declare memberwise initializer";
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
#define REGISTER_TWEAK(Subclass)
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::Expected< tooling::Replacement > insertDecl(llvm::StringRef Code, const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
constexpr llvm::StringLiteral Message
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
static const llvm::StringLiteral REFACTOR_KIND