//===- DeclObjC.cpp - ObjC Declaration AST Node Implementation ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the Objective-C related Decl classes.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/ODRHash.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <queue>
#include <utility>
using namespace clang;
//===----------------------------------------------------------------------===//
// ObjCListBase
//===----------------------------------------------------------------------===//
void ObjCListBase::set(void *const* InList, unsigned Elts, ASTContext &Ctx) {
List = nullptr;
if (Elts == 0) return; // Setting to an empty list is a noop.
List = new (Ctx) void*[Elts];
NumElts = Elts;
memcpy(List, InList, sizeof(void*)*Elts);
}
void ObjCProtocolList::set(ObjCProtocolDecl* const* InList, unsigned Elts,
const SourceLocation *Locs, ASTContext &Ctx) {
if (Elts == 0)
return;
Locations = new (Ctx) SourceLocation[Elts];
memcpy(Locations, Locs, sizeof(SourceLocation) * Elts);
set(InList, Elts, Ctx);
}
//===----------------------------------------------------------------------===//
// ObjCInterfaceDecl
//===----------------------------------------------------------------------===//
ObjCContainerDecl::ObjCContainerDecl(Kind DK, DeclContext *DC,
const IdentifierInfo *Id,
SourceLocation nameLoc,
SourceLocation atStartLoc)
: NamedDecl(DK, DC, nameLoc, Id), DeclContext(DK) {
setAtStartLoc(atStartLoc);
}
void ObjCContainerDecl::anchor() {}
/// getIvarDecl - This method looks up an ivar in this ContextDecl.
///
ObjCIvarDecl *
ObjCContainerDecl::getIvarDecl(IdentifierInfo *Id) const {
lookup_result R = lookup(Id);
for (lookup_iterator Ivar = R.begin(), IvarEnd = R.end();
Ivar != IvarEnd; ++Ivar) {
if (auto *ivar = dyn_cast<ObjCIvarDecl>(*Ivar))
return ivar;
}
return nullptr;
}
// Get the local instance/class method declared in this interface.
ObjCMethodDecl *
ObjCContainerDecl::getMethod(Selector Sel, bool isInstance,
bool AllowHidden) const {
// If this context is a hidden protocol definition, don't find any
// methods there.
if (const auto *Proto = dyn_cast<ObjCProtocolDecl>(this)) {
if (const ObjCProtocolDecl *Def = Proto->getDefinition())
if (!Def->isUnconditionallyVisible() && !AllowHidden)
return nullptr;
}
// Since instance & class methods can have the same name, the loop below
// ensures we get the correct method.
//
// @interface Whatever
// - (int) class_method;
// + (float) class_method;
// @end
lookup_result R = lookup(Sel);
for (lookup_iterator Meth = R.begin(), MethEnd = R.end();
Meth != MethEnd; ++Meth) {
auto *MD = dyn_cast<ObjCMethodDecl>(*Meth);
if (MD && MD->isInstanceMethod() == isInstance)
return MD;
}
return nullptr;
}
/// This routine returns 'true' if a user declared setter method was
/// found in the class, its protocols, its super classes or categories.
/// It also returns 'true' if one of its categories has declared a 'readwrite'
/// property. This is because, user must provide a setter method for the
/// category's 'readwrite' property.
bool ObjCContainerDecl::HasUserDeclaredSetterMethod(
const ObjCPropertyDecl *Property) const {
Selector Sel = Property->getSetterName();
lookup_result R = lookup(Sel);
for (lookup_iterator Meth = R.begin(), MethEnd = R.end();
Meth != MethEnd; ++Meth) {
auto *MD = dyn_cast<ObjCMethodDecl>(*Meth);
if (MD && MD->isInstanceMethod() && !MD->isImplicit())
return true;
}
if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(this)) {
// Also look into categories, including class extensions, looking
// for a user declared instance method.
for (const auto *Cat : ID->visible_categories()) {
if (ObjCMethodDecl *MD = Cat->getInstanceMethod(Sel))
if (!MD->isImplicit())
return true;
if (Cat->IsClassExtension())
continue;
// Also search through the categories looking for a 'readwrite'
// declaration of this property. If one found, presumably a setter will
// be provided (properties declared in categories will not get
// auto-synthesized).
for (const auto *P : Cat->properties())
if (P->getIdentifier() == Property->getIdentifier()) {
if (P->getPropertyAttributes() &
ObjCPropertyAttribute::kind_readwrite)
return true;
break;
}
}
// Also look into protocols, for a user declared instance method.
for (const auto *Proto : ID->all_referenced_protocols())
if (Proto->HasUserDeclaredSetterMethod(Property))
return true;
// And in its super class.
ObjCInterfaceDecl *OSC = ID->getSuperClass();
while (OSC) {
if (OSC->HasUserDeclaredSetterMethod(Property))
return true;
OSC = OSC->getSuperClass();
}
}
if (const auto *PD = dyn_cast<ObjCProtocolDecl>(this))
for (const auto *PI : PD->protocols())
if (PI->HasUserDeclaredSetterMethod(Property))
return true;
return false;
}
ObjCPropertyDecl *
ObjCPropertyDecl::findPropertyDecl(const DeclContext *DC,
const IdentifierInfo *propertyID,
ObjCPropertyQueryKind queryKind) {
// If this context is a hidden protocol definition, don't find any
// property.
if (const auto *Proto = dyn_cast<ObjCProtocolDecl>(DC)) {
if (const ObjCProtocolDecl *Def = Proto->getDefinition())
if (!Def->isUnconditionallyVisible())
return nullptr;
}
// If context is class, then lookup property in its visible extensions.
// This comes before property is looked up in primary class.
if (auto *IDecl = dyn_cast<ObjCInterfaceDecl>(DC)) {
for (const auto *Ext : IDecl->visible_extensions())
if (ObjCPropertyDecl *PD = ObjCPropertyDecl::findPropertyDecl(Ext,
propertyID,
queryKind))
return PD;
}
DeclContext::lookup_result R = DC->lookup(propertyID);
ObjCPropertyDecl *classProp = nullptr;
for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E;
++I)
if (auto *PD = dyn_cast<ObjCPropertyDecl>(*I)) {
// If queryKind is unknown, we return the instance property if one
// exists; otherwise we return the class property.
if ((queryKind == ObjCPropertyQueryKind::OBJC_PR_query_unknown &&
|