blob: 986411fc7d2b637e53df61c6542ce4beba6a43c3 [file] [log] [blame]
Ted Kremenek8f0a1c72010-01-27 23:43:25 +00001//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Handling of format string in printf and friends. The structure of format
11// strings for fprintf() are described in C99 7.19.6.1.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Analysis/Analyses/PrintfFormatString.h"
16
17using namespace clang;
Ted Kremenekd2dcece2010-01-28 02:02:59 +000018using namespace analyze_printf;
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000019
20namespace {
21class FormatSpecifierResult {
22 FormatSpecifier FS;
23 const char *Start;
24 bool HasError;
25public:
26 FormatSpecifierResult(bool err = false)
27 : Start(0), HasError(err) {}
28 FormatSpecifierResult(const char *start,
Ted Kremenekd2dcece2010-01-28 02:02:59 +000029 const FormatSpecifier &fs)
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000030 : FS(fs), Start(start), HasError(false) {}
31
32
33 const char *getStart() const { return Start; }
34 bool hasError() const { return HasError; }
35 bool hasValue() const { return Start != 0; }
36 const FormatSpecifier &getValue() const {
37 assert(hasValue());
38 return FS;
39 }
Ted Kremenekd2dcece2010-01-28 02:02:59 +000040 const FormatSpecifier &getValue() { return FS; }
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000041};
42} // end anonymous namespace
43
44template <typename T>
45class UpdateOnReturn {
46 T &ValueToUpdate;
47 const T &ValueToCopy;
48public:
49 UpdateOnReturn(T &valueToUpdate, const T &valueToCopy)
50 : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {}
51
52 ~UpdateOnReturn() {
53 ValueToUpdate = ValueToCopy;
54 }
55};
56
57static OptionalAmount ParseAmount(const char *&Beg, const char *E) {
58 const char *I = Beg;
59 UpdateOnReturn <const char*> UpdateBeg(Beg, I);
60
61 bool foundDigits = false;
62 unsigned accumulator = 0;
63
64 for ( ; I != E; ++I) {
65 char c = *I;
66 if (c >= '0' && c <= '9') {
67 foundDigits = true;
68 accumulator += (accumulator * 10) + (c - '0');
69 continue;
70 }
71
72 if (foundDigits)
73 return OptionalAmount(accumulator);
74
75 if (c == '*')
76 return OptionalAmount(OptionalAmount::Arg);
77
78 break;
79 }
80
81 return OptionalAmount();
82}
83
Ted Kremenekd2dcece2010-01-28 02:02:59 +000084static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H,
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000085 const char *&Beg, const char *E) {
86
87 const char *I = Beg;
Ted Kremenekc7ae51a2010-01-28 00:02:05 +000088 const char *Start = 0;
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000089 UpdateOnReturn <const char*> UpdateBeg(Beg, I);
90
91 // Look for a '%' character that indicates the start of a format specifier.
Ted Kremeneke729acb2010-01-28 23:56:52 +000092 for ( ; I != E ; ++I) {
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000093 char c = *I;
Ted Kremenek8f0a1c72010-01-27 23:43:25 +000094 if (c == '\0') {
95 // Detect spurious null characters, which are likely errors.
96 H.HandleNullChar(I);
97 return true;
98 }
99 if (c == '%') {
Ted Kremeneke729acb2010-01-28 23:56:52 +0000100 Start = I++; // Record the start of the format specifier.
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000101 break;
102 }
103 }
104
105 // No format specifier found?
106 if (!Start)
107 return false;
108
109 if (I == E) {
110 // No more characters left?
111 H.HandleIncompleteFormatSpecifier(Start, E);
112 return true;
113 }
114
115 FormatSpecifier FS;
116
117 // Look for flags (if any).
118 bool hasMore = true;
119 for ( ; I != E; ++I) {
120 switch (*I) {
121 default: hasMore = false; break;
122 case '-': FS.setIsLeftJustified(); break;
123 case '+': FS.setHasPlusPrefix(); break;
124 case ' ': FS.setHasSpacePrefix(); break;
125 case '#': FS.setHasAlternativeForm(); break;
126 case '0': FS.setHasLeadingZeros(); break;
127 }
128 if (!hasMore)
129 break;
130 }
131
132 if (I == E) {
133 // No more characters left?
134 H.HandleIncompleteFormatSpecifier(Start, E);
135 return true;
136 }
137
138 // Look for the field width (if any).
139 FS.setFieldWidth(ParseAmount(I, E));
140
141 if (I == E) {
142 // No more characters left?
143 H.HandleIncompleteFormatSpecifier(Start, E);
144 return true;
145 }
146
147 // Look for the precision (if any).
148 if (*I == '.') {
149 const char *startPrecision = I++;
150 if (I == E) {
151 H.HandleIncompletePrecision(I - 1);
152 return true;
153 }
154
155 FS.setPrecision(ParseAmount(I, E));
156
157 if (I == E) {
158 // No more characters left?
159 H.HandleIncompletePrecision(startPrecision);
160 return true;
161 }
162 }
163
164 // Look for the length modifier.
165 LengthModifier lm = None;
166 switch (*I) {
167 default:
168 break;
169 case 'h':
170 ++I;
171 lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort;
172 break;
173 case 'l':
174 ++I;
175 lm = (I != E && *I == 'l') ? ++I, AsLongLong : AsLong;
176 break;
177 case 'j': lm = AsIntMax; ++I; break;
178 case 'z': lm = AsSizeT; ++I; break;
179 case 't': lm = AsPtrDiff; ++I; break;
180 case 'L': lm = AsLongDouble; ++I; break;
181 }
182 FS.setLengthModifier(lm);
183
184 if (I == E) {
185 // No more characters left?
186 H.HandleIncompleteFormatSpecifier(Start, E);
187 return true;
188 }
189
190 // Finally, look for the conversion specifier.
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000191 const char *conversionPosition = I++;
192 ConversionSpecifier::Kind k;
193 switch (*conversionPosition) {
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000194 default:
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000195 H.HandleInvalidConversionSpecifier(conversionPosition);
Ted Kremenekc7cbb9b2010-01-28 00:55:28 +0000196 return true;
197 // C99: 7.19.6.1 (section 8).
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000198 case 'd': k = ConversionSpecifier::dArg; break;
199 case 'i': k = ConversionSpecifier::iArg; break;
200 case 'o': k = ConversionSpecifier::oArg; break;
201 case 'u': k = ConversionSpecifier::uArg; break;
202 case 'x': k = ConversionSpecifier::xArg; break;
203 case 'X': k = ConversionSpecifier::XArg; break;
204 case 'f': k = ConversionSpecifier::fArg; break;
205 case 'F': k = ConversionSpecifier::FArg; break;
206 case 'e': k = ConversionSpecifier::eArg; break;
207 case 'E': k = ConversionSpecifier::EArg; break;
208 case 'g': k = ConversionSpecifier::gArg; break;
209 case 'G': k = ConversionSpecifier::GArg; break;
210 case 'a': k = ConversionSpecifier::aArg; break;
211 case 'A': k = ConversionSpecifier::AArg; break;
212 case 'c': k = ConversionSpecifier::IntAsCharArg; break;
213 case 's': k = ConversionSpecifier::CStrArg; break;
214 case 'p': k = ConversionSpecifier::VoidPtrArg; break;
215 case 'n': k = ConversionSpecifier::OutIntPtrArg; break;
216 case '%': k = ConversionSpecifier::PercentArg; break;
Ted Kremenekc7cbb9b2010-01-28 00:55:28 +0000217 // Objective-C.
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000218 case '@': k = ConversionSpecifier::ObjCObjArg; break;
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000219 }
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000220 FS.setConversionSpecifier(ConversionSpecifier(conversionPosition, k));
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000221 return FormatSpecifierResult(Start, FS);
222}
223
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000224namespace clang { namespace analyze_printf {
225bool ParseFormatString(FormatStringHandler &H,
226 const char *I, const char *E) {
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000227 // Keep looking for a format specifier until we have exhausted the string.
228 while (I != E) {
229 const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E);
230 // Did an error of any kind occur when parsing the specifier? If so,
231 // don't do any more processing.
232 if (FSR.hasError())
233 return true;;
234 // Done processing the string?
235 if (!FSR.hasValue())
236 break;
237 // We have a format specifier. Pass it to the callback.
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000238 if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(),
239 I - FSR.getStart()))
Ted Kremenekd5f20962010-01-28 01:00:59 +0000240 return false;
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000241 }
242 assert(I == E && "Format string not exhausted");
243 return false;
244}
Ted Kremeneka8d8fec2010-01-28 02:46:17 +0000245}}
Ted Kremenek8f0a1c72010-01-27 23:43:25 +0000246
247FormatStringHandler::~FormatStringHandler() {}