1 // TODO: header template
3 #include "clang/AST/OSLog.h"
4 #include "clang/AST/Attr.h"
5 #include "clang/AST/Decl.h"
6 #include "clang/AST/DeclCXX.h"
7 #include "clang/AST/ExprObjC.h"
8 #include "clang/AST/FormatString.h"
9 #include "clang/Basic/Builtins.h"
10 #include "llvm/ADT/SmallBitVector.h"
13 using namespace clang
;
15 using clang::analyze_os_log::OSLogBufferItem
;
16 using clang::analyze_os_log::OSLogBufferLayout
;
19 class OSLogFormatStringHandler
20 : public analyze_format_string::FormatStringHandler
{
23 const Expr
*E
= nullptr;
24 std::optional
<OSLogBufferItem::Kind
> Kind
;
25 std::optional
<unsigned> Size
;
26 std::optional
<const Expr
*> Count
;
27 std::optional
<const Expr
*> Precision
;
28 std::optional
<const Expr
*> FieldWidth
;
29 unsigned char Flags
= 0;
32 SmallVector
<ArgData
, 4> ArgsData
;
33 ArrayRef
<const Expr
*> Args
;
36 getKind(analyze_format_string::ConversionSpecifier::Kind K
) {
38 case clang::analyze_format_string::ConversionSpecifier::sArg
: // "%s"
39 return OSLogBufferItem::StringKind
;
40 case clang::analyze_format_string::ConversionSpecifier::SArg
: // "%S"
41 return OSLogBufferItem::WideStringKind
;
42 case clang::analyze_format_string::ConversionSpecifier::PArg
: { // "%P"
43 return OSLogBufferItem::PointerKind
;
44 case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg
: // "%@"
45 return OSLogBufferItem::ObjCObjKind
;
46 case clang::analyze_format_string::ConversionSpecifier::PrintErrno
: // "%m"
47 return OSLogBufferItem::ErrnoKind
;
49 return OSLogBufferItem::ScalarKind
;
55 OSLogFormatStringHandler(ArrayRef
<const Expr
*> Args
) : Args(Args
) {
56 ArgsData
.reserve(Args
.size());
59 bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
&FS
,
60 const char *StartSpecifier
, unsigned SpecifierLen
,
61 const TargetInfo
&) override
{
62 if (!FS
.consumesDataArgument() &&
63 FS
.getConversionSpecifier().getKind() !=
64 clang::analyze_format_string::ConversionSpecifier::PrintErrno
)
67 ArgsData
.emplace_back();
68 unsigned ArgIndex
= FS
.getArgIndex();
69 if (ArgIndex
< Args
.size())
70 ArgsData
.back().E
= Args
[ArgIndex
];
73 ArgsData
.back().Kind
= getKind(FS
.getConversionSpecifier().getKind());
74 if (ArgsData
.back().Kind
!= OSLogBufferItem::ErrnoKind
&&
81 switch (FS
.getConversionSpecifier().getKind()) {
82 case clang::analyze_format_string::ConversionSpecifier::sArg
: // "%s"
83 case clang::analyze_format_string::ConversionSpecifier::SArg
: { // "%S"
84 auto &precision
= FS
.getPrecision();
85 switch (precision
.getHowSpecified()) {
86 case clang::analyze_format_string::OptionalAmount::NotSpecified
: // "%s"
88 case clang::analyze_format_string::OptionalAmount::Constant
: // "%.16s"
89 ArgsData
.back().Size
= precision
.getConstantAmount();
91 case clang::analyze_format_string::OptionalAmount::Arg
: // "%.*s"
92 ArgsData
.back().Count
= Args
[precision
.getArgIndex()];
94 case clang::analyze_format_string::OptionalAmount::Invalid
:
99 case clang::analyze_format_string::ConversionSpecifier::PArg
: { // "%P"
100 auto &precision
= FS
.getPrecision();
101 switch (precision
.getHowSpecified()) {
102 case clang::analyze_format_string::OptionalAmount::NotSpecified
: // "%P"
103 return false; // length must be supplied with pointer format specifier
104 case clang::analyze_format_string::OptionalAmount::Constant
: // "%.16P"
105 ArgsData
.back().Size
= precision
.getConstantAmount();
107 case clang::analyze_format_string::OptionalAmount::Arg
: // "%.*P"
108 ArgsData
.back().Count
= Args
[precision
.getArgIndex()];
110 case clang::analyze_format_string::OptionalAmount::Invalid
:
116 if (FS
.getPrecision().hasDataArgument()) {
117 ArgsData
.back().Precision
= Args
[FS
.getPrecision().getArgIndex()];
121 if (FS
.getFieldWidth().hasDataArgument()) {
122 ArgsData
.back().FieldWidth
= Args
[FS
.getFieldWidth().getArgIndex()];
125 if (FS
.isSensitive())
126 ArgsData
.back().Flags
|= OSLogBufferItem::IsSensitive
;
127 else if (FS
.isPrivate())
128 ArgsData
.back().Flags
|= OSLogBufferItem::IsPrivate
;
129 else if (FS
.isPublic())
130 ArgsData
.back().Flags
|= OSLogBufferItem::IsPublic
;
132 ArgsData
.back().MaskType
= FS
.getMaskType();
136 void computeLayout(ASTContext
&Ctx
, OSLogBufferLayout
&Layout
) const {
137 Layout
.Items
.clear();
138 for (auto &Data
: ArgsData
) {
139 if (!Data
.MaskType
.empty()) {
140 CharUnits Size
= CharUnits::fromQuantity(8);
141 Layout
.Items
.emplace_back(OSLogBufferItem::MaskKind
, nullptr,
142 Size
, 0, Data
.MaskType
);
145 if (Data
.FieldWidth
) {
146 CharUnits Size
= Ctx
.getTypeSizeInChars((*Data
.FieldWidth
)->getType());
147 Layout
.Items
.emplace_back(OSLogBufferItem::ScalarKind
, *Data
.FieldWidth
,
150 if (Data
.Precision
) {
151 CharUnits Size
= Ctx
.getTypeSizeInChars((*Data
.Precision
)->getType());
152 Layout
.Items
.emplace_back(OSLogBufferItem::ScalarKind
, *Data
.Precision
,
156 // "%.*P" has an extra "count" that we insert before the argument.
157 CharUnits Size
= Ctx
.getTypeSizeInChars((*Data
.Count
)->getType());
158 Layout
.Items
.emplace_back(OSLogBufferItem::CountKind
, *Data
.Count
, Size
,
162 Layout
.Items
.emplace_back(Ctx
, CharUnits::fromQuantity(*Data
.Size
),
166 if (*Data
.Kind
== OSLogBufferItem::ErrnoKind
)
167 Size
= CharUnits::Zero();
169 Size
= Ctx
.getTypeSizeInChars(Data
.E
->getType());
170 Layout
.Items
.emplace_back(*Data
.Kind
, Data
.E
, Size
, Data
.Flags
);
172 auto Size
= Ctx
.getTypeSizeInChars(Data
.E
->getType());
173 Layout
.Items
.emplace_back(OSLogBufferItem::ScalarKind
, Data
.E
, Size
,
179 } // end anonymous namespace
181 bool clang::analyze_os_log::computeOSLogBufferLayout(
182 ASTContext
&Ctx
, const CallExpr
*E
, OSLogBufferLayout
&Layout
) {
183 ArrayRef
<const Expr
*> Args(E
->getArgs(), E
->getArgs() + E
->getNumArgs());
185 const Expr
*StringArg
;
186 ArrayRef
<const Expr
*> VarArgs
;
187 switch (E
->getBuiltinCallee()) {
188 case Builtin::BI__builtin_os_log_format_buffer_size
:
189 assert(E
->getNumArgs() >= 1 &&
190 "__builtin_os_log_format_buffer_size takes at least 1 argument");
191 StringArg
= E
->getArg(0);
192 VarArgs
= Args
.slice(1);
194 case Builtin::BI__builtin_os_log_format
:
195 assert(E
->getNumArgs() >= 2 &&
196 "__builtin_os_log_format takes at least 2 arguments");
197 StringArg
= E
->getArg(1);
198 VarArgs
= Args
.slice(2);
201 llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
204 const StringLiteral
*Lit
= cast
<StringLiteral
>(StringArg
->IgnoreParenCasts());
205 assert(Lit
&& (Lit
->isOrdinary() || Lit
->isUTF8()));
206 StringRef Data
= Lit
->getString();
207 OSLogFormatStringHandler
H(VarArgs
);
208 ParsePrintfString(H
, Data
.begin(), Data
.end(), Ctx
.getLangOpts(),
209 Ctx
.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
211 H
.computeLayout(Ctx
, Layout
);