33#include "llvm/ADT/STLExtras.h"
34#include "llvm/ADT/SmallString.h"
35#include "llvm/ADT/StringMap.h"
36#include "llvm/Support/raw_ostream.h"
44class APIMisuse :
public BugType {
46 APIMisuse(
const CheckerBase *checker,
const char *name)
57 return ID->getIdentifier()->getName();
73 bool IncludeSuperclasses =
true) {
74 static llvm::StringMap<FoundationClass> Classes;
75 if (Classes.empty()) {
86 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
87 if (result ==
FC_None && IncludeSuperclasses)
99class NilArgChecker :
public Checker<check::PreObjCMessage,
100 check::PostStmt<ObjCDictionaryLiteral>,
101 check::PostStmt<ObjCArrayLiteral>,
102 EventDispatcher<ImplicitNullDerefEvent>> {
103 mutable std::unique_ptr<APIMisuse> BT;
105 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
106 mutable Selector ArrayWithObjectSel;
108 mutable Selector InsertObjectAtIndexSel;
109 mutable Selector ReplaceObjectAtIndexWithObjectSel;
110 mutable Selector SetObjectAtIndexedSubscriptSel;
111 mutable Selector ArrayByAddingObjectSel;
112 mutable Selector DictionaryWithObjectForKeySel;
113 mutable Selector SetObjectForKeySel;
114 mutable Selector SetObjectForKeyedSubscriptSel;
115 mutable Selector RemoveObjectForKeySel;
132void NilArgChecker::warnIfNilExpr(
const Expr *
E,
135 auto Location =
C.getSVal(
E).getAs<
Loc>();
153 dispatchEvent({*Location,
false, N, &
C.getBugReporter(),
164 bool CanBeSubscript)
const {
167 if (!State->isNull(msg.
getArgSVal(Arg)).isConstrainedTrue())
177 llvm::raw_svector_ostream os(sbuf);
182 os <<
"Array element cannot be nil";
185 os <<
"Value stored into '";
192 llvm_unreachable(
"Missing foundation class for the subscript expr");
197 os <<
"Value argument ";
200 os <<
"Key argument ";
204 os <<
"' cannot be nil";
208 os <<
"' cannot be nil";
223 BT.reset(
new APIMisuse(
this,
"nil argument"));
225 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
228 C.emitReport(std::move(R));
239 static const unsigned InvalidArgIndex =
UINT_MAX;
240 unsigned Arg = InvalidArgIndex;
241 bool CanBeSubscript =
false;
246 if (S.isUnarySelector())
249 if (StringSelectors.empty()) {
264 StringSelectors[KnownSel] = 0;
266 auto I = StringSelectors.find(S);
267 if (I == StringSelectors.end())
273 if (S.isUnarySelector())
276 if (ArrayWithObjectSel.isNull()) {
280 InsertObjectAtIndexSel =
282 ReplaceObjectAtIndexWithObjectSel =
284 SetObjectAtIndexedSubscriptSel =
289 if (S == ArrayWithObjectSel || S == AddObjectSel ||
290 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
292 }
else if (S == SetObjectAtIndexedSubscriptSel) {
294 CanBeSubscript =
true;
295 }
else if (S == ReplaceObjectAtIndexWithObjectSel) {
301 if (S.isUnarySelector())
304 if (DictionaryWithObjectForKeySel.isNull()) {
306 DictionaryWithObjectForKeySel =
309 SetObjectForKeyedSubscriptSel =
314 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
316 warnIfNilArg(
C, msg, 1, Class);
317 }
else if (S == SetObjectForKeyedSubscriptSel) {
318 CanBeSubscript =
true;
320 }
else if (S == RemoveObjectForKeySel) {
326 if ((Arg != InvalidArgIndex))
327 warnIfNilArg(
C, msg, Arg, Class, CanBeSubscript);
333 for (
unsigned i = 0; i < NumOfElements; ++i) {
334 warnIfNilExpr(AL->
getElement(i),
"Array element cannot be nil",
C);
341 for (
unsigned i = 0; i < NumOfElements; ++i) {
343 warnIfNilExpr(Element.Key,
"Dictionary key cannot be nil",
C);
344 warnIfNilExpr(Element.Value,
"Dictionary value cannot be nil",
C);
353class CFNumberChecker :
public Checker< check::PreStmt<CallExpr> > {
354 mutable std::unique_ptr<APIMisuse> BT;
357 CFNumberChecker() =
default;
383 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
386 return FixedSize[i-1];
410static const char* GetCFNumberTypeStr(uint64_t i) {
411 static const char* Names[] = {
412 "kCFNumberSInt8Type",
413 "kCFNumberSInt16Type",
414 "kCFNumberSInt32Type",
415 "kCFNumberSInt64Type",
416 "kCFNumberFloat32Type",
417 "kCFNumberFloat64Type",
419 "kCFNumberShortType",
422 "kCFNumberLongLongType",
423 "kCFNumberFloatType",
424 "kCFNumberDoubleType",
425 "kCFNumberCFIndexType",
426 "kCFNumberNSIntegerType",
427 "kCFNumberCGFloatType"
434void CFNumberChecker::checkPreStmt(
const CallExpr *CE,
443 ICreate = &Ctx.
Idents.
get(
"CFNumberCreate");
444 IGetValue = &Ctx.
Idents.
get(
"CFNumberGetValue");
455 std::optional<nonloc::ConcreteInt>
V =
456 dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
460 uint64_t NumberKind =
V->getValue().getLimitedValue();
461 std::optional<uint64_t> OptCFNumberSize =
GetCFNumberSize(Ctx, NumberKind);
464 if (!OptCFNumberSize)
467 uint64_t CFNumberSize = *OptCFNumberSize;
494 if (PrimitiveTypeSize == CFNumberSize)
502 llvm::raw_svector_ostream os(sbuf);
506 os << (PrimitiveTypeSize == 8 ?
"An " :
"A ")
507 << PrimitiveTypeSize <<
"-bit integer is used to initialize a "
508 <<
"CFNumber object that represents "
509 << (CFNumberSize == 8 ?
"an " :
"a ")
510 << CFNumberSize <<
"-bit integer; ";
512 os <<
"A CFNumber object that represents "
513 << (CFNumberSize == 8 ?
"an " :
"a ")
514 << CFNumberSize <<
"-bit integer is used to initialize "
515 << (PrimitiveTypeSize == 8 ?
"an " :
"a ")
516 << PrimitiveTypeSize <<
"-bit integer; ";
519 if (PrimitiveTypeSize < CFNumberSize)
520 os << (CFNumberSize - PrimitiveTypeSize)
521 <<
" bits of the CFNumber value will "
522 << (isCreate ?
"be garbage." :
"overwrite adjacent storage.");
524 os << (PrimitiveTypeSize - CFNumberSize)
525 <<
" bits of the integer value will be "
526 << (isCreate ?
"lost." :
"garbage.");
529 BT.reset(
new APIMisuse(
this,
"Bad use of CFNumber APIs"));
531 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
533 C.emitReport(std::move(report));
542class CFRetainReleaseChecker :
public Checker<check::PreCall> {
543 mutable APIMisuse BT{
this,
"null passed to CF memory management function"};
545 {CDM::CLibrary, {
"CFRetain"}, 1},
546 {CDM::CLibrary, {
"CFRelease"}, 1},
547 {CDM::CLibrary, {
"CFMakeCollectable"}, 1},
548 {CDM::CLibrary, {
"CFAutorelease"}, 1},
556void CFRetainReleaseChecker::checkPreCall(
const CallEvent &
Call,
559 if (!ModelledCalls.contains(
Call))
571 std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
579 raw_svector_ostream OS(Str);
580 OS <<
"Null pointer argument in call to "
581 << cast<FunctionDecl>(
Call.getDecl())->getName();
583 auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
584 report->addRange(
Call.getArgSourceRange(0));
586 C.emitReport(std::move(report));
591 C.addTransition(stateNonNull);
599class ClassReleaseChecker :
public Checker<check::PreObjCMessage> {
604 mutable std::unique_ptr<BugType> BT;
611void ClassReleaseChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
614 BT.reset(
new APIMisuse(
615 this,
"message incorrectly sent to class instead of class instance"));
630 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
635 llvm::raw_svector_ostream os(buf);
639 os <<
"' message should be sent to instances "
640 "of class '" <<
Class->getName()
641 <<
"' and not the class directly";
643 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
645 C.emitReport(std::move(report));
655class VariadicMethodTypeChecker :
public Checker<check::PreObjCMessage> {
657 mutable Selector dictionaryWithObjectsAndKeysS;
659 mutable Selector orderedSetWithObjectsS;
661 mutable Selector initWithObjectsAndKeysS;
662 mutable std::unique_ptr<BugType> BT;
674VariadicMethodTypeChecker::isVariadicMessage(
const ObjCMethodCall &msg)
const {
695 return S == initWithObjectsS;
697 return S == initWithObjectsAndKeysS;
706 return S == arrayWithObjectsS;
708 return S == orderedSetWithObjectsS;
710 return S == setWithObjectsS;
712 return S == dictionaryWithObjectsAndKeysS;
719void VariadicMethodTypeChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
722 BT.reset(
new APIMisuse(
this,
723 "Arguments passed to variadic method aren't all "
724 "Objective-C pointer types"));
728 dictionaryWithObjectsAndKeysS =
737 if (!isVariadicMessage(msg))
746 unsigned variadicArgsEnd = msg.
getNumArgs() - 1;
748 if (variadicArgsEnd <= variadicArgsBegin)
752 std::optional<ExplodedNode *> errorNode;
754 for (
unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
768 if (
C.getASTContext().isObjCNSObjectType(ArgTy))
777 errorNode =
C.generateNonFatalErrorNode();
783 llvm::raw_svector_ostream os(sbuf);
787 os <<
"Argument to '" <<
TypeName <<
"' method '";
789 os <<
"Argument to method '";
792 os <<
"' should be an Objective-C pointer type, not '";
793 ArgTy.
print(os,
C.getLangOpts());
797 std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
799 C.emitReport(std::move(R));
814 :
public Checker<check::PostStmt<ObjCForCollectionStmt>,
815 check::PostObjCMessage,
817 check::PointerEscape > {
824 ObjCLoopChecker() =
default;
867 std::optional<DefinedSVal> KnownCollection =
869 if (!KnownCollection)
873 std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
874 if (StNil && !StNonNil) {
900 std::optional<Loc> ElementLoc;
901 if (
const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
902 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
903 assert(ElemDecl->
getInit() ==
nullptr);
904 ElementLoc = State->getLValue(ElemDecl, LCtx);
906 ElementLoc = State->getSVal(Element, LCtx).getAs<
Loc>();
913 SVal Val = State->getSVal(*ElementLoc);
914 return State->assume(cast<DefinedOrUnknownSVal>(Val),
true);
921 SymbolRef CollectionS,
bool Assumption) {
922 if (!State || !CollectionS)
925 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
927 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
929 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
930 return (Assumption == *KnownNonEmpty) ? State :
nullptr;
934 SVal CountGreaterThanZeroVal =
937 SvalBuilder.
makeIntVal(0, (*CountS)->getType()),
939 std::optional<DefinedSVal> CountGreaterThanZero =
941 if (!CountGreaterThanZero) {
947 return State->assume(*CountGreaterThanZero, Assumption);
968 if (std::optional<BlockEdge> BE =
P.getAs<
BlockEdge>()) {
969 return BE->getSrc()->getLoopTarget() == FCS;
998 C.generateSink(
C.getState(),
C.getPredecessor());
999 else if (State !=
C.getState())
1000 C.addTransition(State);
1003bool ObjCLoopChecker::isCollectionCountMethod(
const ObjCMethodCall &M,
1007 if (!CountSelectorII)
1008 CountSelectorII = &
C.getASTContext().Idents.get(
"count");
1011 return S.isUnarySelector() &&
1012 (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1015void ObjCLoopChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1037 if (!isCollectionCountMethod(M,
C))
1041 SymbolRef CountS =
C.getSVal(MsgExpr).getAsSymbol();
1045 C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1046 State = State->set<ContainerCountMap>(ContainerS, CountS);
1048 if (
const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1049 State = State->remove<ContainerNonEmptyMap>(ContainerS);
1053 C.addTransition(State);
1071 StaticClass = Message->getOriginExpr()->getReceiverInterface();
1092 return Message->getReceiverSVal().getAsSymbol();
1108 if (Sym == ImmutableReceiver)
1113 State = State->remove<ContainerCountMap>(Sym);
1114 State = State->remove<ContainerNonEmptyMap>(Sym);
1119void ObjCLoopChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1124 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1125 for (
SymbolRef Sym : llvm::make_first_range(Tracked)) {
1126 if (SymReaper.
isDead(Sym)) {
1127 State = State->remove<ContainerCountMap>(Sym);
1128 State = State->remove<ContainerNonEmptyMap>(Sym);
1132 C.addTransition(State);
1139class ObjCNonNilReturnValueChecker
1140 :
public Checker<check::PostObjCMessage,
1141 check::PostStmt<ObjCArrayLiteral>,
1142 check::PostStmt<ObjCDictionaryLiteral>,
1143 check::PostStmt<ObjCBoxedExpr> > {
1146 mutable Selector ObjectAtIndexedSubscript;
1150 ObjCNonNilReturnValueChecker() =
default;
1156 C.addTransition(assumeExprIsNonNull(
E,
C.getState(),
C));
1160 assumeExprIsNonNull(
E,
C);
1163 assumeExprIsNonNull(
E,
C);
1166 assumeExprIsNonNull(
E,
C);
1174ObjCNonNilReturnValueChecker::assumeExprIsNonNull(
const Expr *NonNullExpr,
1177 SVal Val =
C.getSVal(NonNullExpr);
1178 if (std::optional<DefinedOrUnknownSVal> DV =
1180 return State->assume(*DV,
true);
1184void ObjCNonNilReturnValueChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1192 ObjectAtIndexedSubscript =
GetUnarySelector(
"objectAtIndexedSubscript", Ctx);
1207 if (!
C.inTopFrame() && M.
getDecl() &&
1220 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1234 C.addTransition(State);
1245bool ento::shouldRegisterNilArgChecker(
const CheckerManager &mgr) {
1253bool ento::shouldRegisterCFNumberChecker(
const CheckerManager &mgr) {
1261bool ento::shouldRegisterCFRetainReleaseChecker(
const CheckerManager &mgr) {
1269bool ento::shouldRegisterClassReleaseChecker(
const CheckerManager &mgr) {
1273void ento::registerVariadicMethodTypeChecker(
CheckerManager &mgr) {
1277bool ento::shouldRegisterVariadicMethodTypeChecker(
const CheckerManager &mgr) {
1285bool ento::shouldRegisterObjCLoopChecker(
const CheckerManager &mgr) {
1289void ento::registerObjCNonNilReturnValueChecker(
CheckerManager &mgr) {
1293bool ento::shouldRegisterObjCNonNilReturnValueChecker(
const CheckerManager &mgr) {
Defines the clang::ASTContext interface.
static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, const ObjCForCollectionStmt *FCS)
If the fist block edge is a back edge, we are reentering the loop.
static ProgramStateRef checkCollectionNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection is non-nil.
static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, bool IncludeSuperclasses=true)
static bool isKnownNonNilCollectionType(QualType T)
static ProgramStateRef assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, SymbolRef CollectionS, bool Assumption)
Returns NULL state if the collection is known to contain elements (or is known not to contain element...
static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call)
static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg)
static std::optional< uint64_t > GetCFNumberSize(ASTContext &Ctx, uint64_t i)
static ProgramStateRef checkElementNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection elements are non-nil.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Defines the Objective-C statement AST node classes.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
CanQualType getCanonicalType(QualType T) const
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
DeclContext * getDeclContext()
The return type of classify().
This represents one expression.
Represents a function declaration or definition.
One of these records is kept for each identifier that is lexed.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
ObjCArrayLiteral - used for objective-c array containers; as in: @["Hello", NSApp,...
Expr * getElement(unsigned Index)
getElement - Return the Element at the specified index.
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c array literal.
ObjCBoxedExpr - used for generalized expression boxing.
ObjCDictionaryLiteral - AST node to represent objective-c dictionary literals; as in:"name" : NSUserN...
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c dictionary literal.
ObjCDictionaryElement getKeyValueElement(unsigned Index) const
Represents Objective-C's collection statement.
Represents an ObjC class declaration.
ObjCMethodDecl - Represents an instance or class method declaration.
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
ObjCInterfaceDecl * getClassInterface()
Represents a pointer to an Objective C object.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
A (possibly-)qualified type.
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Smart pointer class that efficiently represents Objective-C method names.
void print(llvm::raw_ostream &OS) const
Prints the full selector name (e.g. "foo:bar:").
unsigned getNumArgs() const
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isBlockPointerType() const
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
bool isObjCObjectPointerType() const
const T * getAs() const
Member-template getAs<specific type>'.
Represents a variable declaration or definition.
const Expr * getInit() const
An immutable set of CallDescriptions.
Represents an abstract call to a function or method along a particular path.
virtual SourceRange getArgSourceRange(unsigned Index) const
Returns the source range for errors associated with this argument.
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
static bool hasMoreIteration(ProgramStateRef State, const ObjCForCollectionStmt *O, const LocationContext *LC)
Represents any expression that calls an Objective-C method.
const ObjCMethodDecl * getDecl() const override
Returns the declaration of the function or method that will be called.
bool isInstanceMessage() const
const Expr * getArgExpr(unsigned Index) const override
Returns the expression associated with a given argument.
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
unsigned getNumArgs() const override
Returns the number of arguments (explicit and implicit).
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
SourceRange getSourceRange() const override
Returns a source range for the entire call, suitable for outputting in diagnostics.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
Selector getSelector() const
A Range represents the closed range [from, to].
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
QualType getConditionType() const
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
TypedValueRegion - An abstract class representing regions having a typed value.
virtual QualType getValueType() const =0
Represents symbolic expression that isn't a location.
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
const char *const AppleAPIMisuse
bool isCFObjectRef(QualType T)
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
bool Null(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
The JSON file list parser is used to communicate input to InstallAPI.
static Selector getKeywordSelector(ASTContext &Ctx, const IdentifierInfos *...IIs)
@ NonNull
Values of this type can never be null.
Selector GetUnarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing an unary selector.
Selector GetNullarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing a nullary selector.
const FunctionProtoType * T
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.
@ Class
The "class" keyword introduces the elaborated-type-specifier.
Diagnostic wrappers for TextAPI types for error reporting.
An element in an Objective-C dictionary literal.