Skip to content

Commit df97c30

Browse files
glankkdanmar
andauthored
Fix #686: Add warning for bad line directives (#687)
Co-authored-by: Daniel Marjamäki <daniel.marjamaki@gmail.com>
1 parent c892d73 commit df97c30

4 files changed

Lines changed: 445 additions & 124 deletions

File tree

main.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ int main(int argc, char **argv)
224224
{
225225
simplecpp::TokenList *rawtokens;
226226
if (toklist_inf == Fstream) {
227-
rawtokens = new simplecpp::TokenList(f,files,filename,&outputList);
227+
rawtokens = new simplecpp::TokenList(f,files,filename,dui,&outputList);
228228
}
229229
else if (toklist_inf == Sstream || toklist_inf == CharBuffer) {
230230
std::ostringstream oss;
@@ -233,14 +233,14 @@ int main(int argc, char **argv)
233233
const std::string s = oss.str();
234234
if (toklist_inf == Sstream) {
235235
std::istringstream iss(s);
236-
rawtokens = new simplecpp::TokenList(iss,files,filename,&outputList);
236+
rawtokens = new simplecpp::TokenList(iss,files,filename,dui,&outputList);
237237
}
238238
else {
239-
rawtokens = new simplecpp::TokenList({s.data(),s.size()},files,filename,&outputList);
239+
rawtokens = new simplecpp::TokenList({s.data(),s.size()},files,filename,dui,&outputList);
240240
}
241241
} else {
242242
f.close();
243-
rawtokens = new simplecpp::TokenList(filename,files,&outputList);
243+
rawtokens = new simplecpp::TokenList(filename,files,dui,&outputList);
244244
}
245245
rawtokens->removeComments();
246246
simplecpp::FileDataCache filedata;
@@ -276,6 +276,7 @@ int main(int argc, char **argv)
276276
std::cerr << "directive as macro parameter: ";
277277
break;
278278
case simplecpp::Output::PORTABILITY_BACKSLASH:
279+
case simplecpp::Output::PORTABILITY_LINE_DIRECTIVE:
279280
std::cerr << "portability: ";
280281
break;
281282
case simplecpp::Output::PORTABILITY_NO_EOF_NEWLINE:

simplecpp.cpp

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -475,26 +475,26 @@ namespace {
475475

476476
simplecpp::TokenList::TokenList(std::vector<std::string> &filenames) : frontToken(nullptr), backToken(nullptr), files(filenames) {}
477477

478-
simplecpp::TokenList::TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList)
478+
simplecpp::TokenList::TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename, const DUI &dui, OutputList *outputList)
479479
: frontToken(nullptr), backToken(nullptr), files(filenames)
480480
{
481481
StdIStream stream(istr);
482-
readfile(stream,filename,outputList);
482+
readfile(stream,filename,dui,outputList);
483483
}
484484

485-
simplecpp::TokenList::TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList, int /*unused*/)
485+
simplecpp::TokenList::TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename, const DUI &dui, OutputList *outputList, int /*unused*/)
486486
: frontToken(nullptr), backToken(nullptr), files(filenames)
487487
{
488488
StdCharBufStream stream(data, size);
489-
readfile(stream,filename,outputList);
489+
readfile(stream,filename,dui,outputList);
490490
}
491491

492-
simplecpp::TokenList::TokenList(const std::string &filename, std::vector<std::string> &filenames, OutputList *outputList)
492+
simplecpp::TokenList::TokenList(const std::string &filename, std::vector<std::string> &filenames, const DUI &dui, OutputList *outputList)
493493
: frontToken(nullptr), backToken(nullptr), files(filenames)
494494
{
495495
try {
496496
FileStream stream(filename, filenames);
497-
readfile(stream,filename,outputList);
497+
readfile(stream,filename,dui,outputList);
498498
} catch (const simplecpp::Output & e) {
499499
outputList->emplace_back(e);
500500
}
@@ -659,13 +659,16 @@ void simplecpp::TokenList::lineDirective(unsigned int fileIndex_, unsigned int l
659659

660660
static const std::string COMMENT_END("*/");
661661

662-
void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, OutputList *outputList)
662+
void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, const DUI &dui, OutputList *outputList)
663663
{
664664
unsigned int multiline = 0U;
665665
bool trailing_nl = true;
666666

667667
const Token *oldLastToken = nullptr;
668668

669+
const cstd_t cstd = getCStd(dui.std);
670+
const cppstd_t cppstd = getCppStd(dui.std);
671+
669672
Location location(fileIndex(filename), 1, 1);
670673
while (stream.good()) {
671674
unsigned char ch = stream.readChar();
@@ -727,10 +730,64 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename,
727730
if (ppTok->str() == "line")
728731
ppTok = advanceAndSkipComments(ppTok);
729732

733+
if (ppTok && (ppTok->str()[0] == '-' || ppTok->str()[0] == '+')) {
734+
if (outputList) {
735+
simplecpp::Output err{
736+
simplecpp::Output::SYNTAX_ERROR,
737+
location,
738+
"Invalid character in line directive: '" + ppTok->str() + "'."
739+
};
740+
outputList->emplace_back(std::move(err));
741+
}
742+
clear();
743+
return;
744+
}
745+
730746
if (!ppTok || !ppTok->number)
731747
continue;
732748

733-
const unsigned int line = std::atol(ppTok->str().c_str());
749+
constexpr unsigned long line_limit = std::numeric_limits<decltype(Location::line)>::max();
750+
unsigned long line;
751+
try {
752+
line = std::min(line_limit, std::stoul(ppTok->str()));
753+
} catch (...) {
754+
line = line_limit;
755+
}
756+
757+
unsigned long maxline;
758+
if ((cstd != CUnknown && cstd < C99) || (cppstd != CPPUnknown && cppstd < CPP11))
759+
maxline = 32767;
760+
else
761+
maxline = 2147483647;
762+
763+
if (line == 0 || line > maxline) {
764+
if (outputList) {
765+
const bool unknown_std = cstd == CUnknown && cppstd == CPPUnknown;
766+
std::string msg = "Line number out of range: " + ppTok->str() + ". ";
767+
if (line == 0) {
768+
msg += "Line number zero is undefined behavior.";
769+
} else {
770+
msg += "Line numbers above " + std::to_string(maxline) + " are ";
771+
if (unknown_std)
772+
msg += "undefined behavior or conditionally supported";
773+
else if (cppstd >= CPP26)
774+
msg += "conditionally supported";
775+
else
776+
msg += "undefined behavior";
777+
if (cstd != CUnknown)
778+
msg += std::string(" in ") + getCStdName(cstd);
779+
else if (cppstd != CPPUnknown)
780+
msg += std::string(" in ") + getCppStdName(cppstd);
781+
msg += ".";
782+
}
783+
simplecpp::Output err{
784+
simplecpp::Output::PORTABILITY_LINE_DIRECTIVE,
785+
location, msg
786+
};
787+
outputList->emplace_back(std::move(err));
788+
}
789+
}
790+
734791
ppTok = advanceAndSkipComments(ppTok);
735792

736793
unsigned int fileindex;
@@ -3163,7 +3220,7 @@ std::pair<simplecpp::FileData *, bool> simplecpp::FileDataCache::tryload(FileDat
31633220
return {id_it->second, false};
31643221
}
31653222

3166-
auto *const data = new FileData {path, TokenList(path, filenames, outputList)};
3223+
auto *const data = new FileData {path, TokenList(path, filenames, {}, outputList)};
31673224

31683225
if (dui.removeComments)
31693226
data->tokens.removeComments();
@@ -3999,6 +4056,20 @@ simplecpp::cstd_t simplecpp::getCStd(const std::string &std)
39994056
return CUnknown;
40004057
}
40014058

4059+
const char *simplecpp::getCStdName(cstd_t std)
4060+
{
4061+
switch (std) {
4062+
case CUnknown: return "C";
4063+
case C89: return "C89";
4064+
case C99: return "C99";
4065+
case C11: return "C11";
4066+
case C17: return "C17";
4067+
case C23: return "C23";
4068+
case C2Y: return "C2Y";
4069+
}
4070+
return "";
4071+
}
4072+
40024073
std::string simplecpp::getCStdString(cstd_t std)
40034074
{
40044075
switch (std) {
@@ -4051,6 +4122,21 @@ simplecpp::cppstd_t simplecpp::getCppStd(const std::string &std)
40514122
return CPPUnknown;
40524123
}
40534124

4125+
const char *simplecpp::getCppStdName(cppstd_t std)
4126+
{
4127+
switch (std) {
4128+
case CPPUnknown: return "C++";
4129+
case CPP03: return "C++03";
4130+
case CPP11: return "C++11";
4131+
case CPP14: return "C++14";
4132+
case CPP17: return "C++17";
4133+
case CPP20: return "C++20";
4134+
case CPP23: return "C++23";
4135+
case CPP26: return "C++26";
4136+
}
4137+
return "";
4138+
}
4139+
40544140
std::string simplecpp::getCppStdString(cppstd_t std)
40554141
{
40564142
switch (std) {

simplecpp.h

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ namespace simplecpp {
249249
SYNTAX_ERROR,
250250
DIRECTIVE_AS_MACRO_PARAMETER,
251251
PORTABILITY_BACKSLASH,
252+
PORTABILITY_LINE_DIRECTIVE,
252253
PORTABILITY_NO_EOF_NEWLINE,
253254
UNHANDLED_CHAR_ERROR,
254255
EXPLICIT_INCLUDE_NOT_FOUND,
@@ -262,52 +263,67 @@ namespace simplecpp {
262263

263264
using OutputList = std::list<Output>;
264265

266+
/**
267+
* Command line preprocessor settings.
268+
* On the command line these are configured by -D, -U, -I, --include, -std
269+
*/
270+
struct SIMPLECPP_LIB DUI {
271+
DUI() = default;
272+
std::list<std::string> defines;
273+
std::set<std::string> undefined;
274+
std::list<std::string> includePaths;
275+
std::list<std::string> includes;
276+
std::string std;
277+
bool clearIncludeCache{};
278+
bool removeComments{}; /** remove comment tokens from included files */
279+
};
280+
265281
/** List of tokens. */
266282
class SIMPLECPP_LIB TokenList {
267283
public:
268284
class Stream;
269285

270286
explicit TokenList(std::vector<std::string> &filenames);
271287
/** generates a token list from the given std::istream parameter */
272-
TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr);
288+
TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr);
273289
/** generates a token list from the given buffer */
274290
template<size_t size>
275-
TokenList(const char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
276-
: TokenList(reinterpret_cast<const unsigned char*>(data), size-1, filenames, filename, outputList, 0)
291+
TokenList(const char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
292+
: TokenList(reinterpret_cast<const unsigned char*>(data), size-1, filenames, filename, dui, outputList, 0)
277293
{}
278294
/** generates a token list from the given buffer */
279295
template<size_t size>
280-
TokenList(const unsigned char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
281-
: TokenList(data, size-1, filenames, filename, outputList, 0)
296+
TokenList(const unsigned char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
297+
: TokenList(data, size-1, filenames, filename, dui, outputList, 0)
282298
{}
283299
#if SIMPLECPP_TOKENLIST_ALLOW_PTR
284300
/** generates a token list from the given buffer */
285-
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
286-
: TokenList(data, size, filenames, filename, outputList, 0)
301+
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
302+
: TokenList(data, size, filenames, filename, dui, outputList, 0)
287303
{}
288304
/** generates a token list from the given buffer */
289-
TokenList(const char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
290-
: TokenList(reinterpret_cast<const unsigned char*>(data), size, filenames, filename, outputList, 0)
305+
TokenList(const char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
306+
: TokenList(reinterpret_cast<const unsigned char*>(data), size, filenames, filename, dui, outputList, 0)
291307
{}
292308
#endif // SIMPLECPP_TOKENLIST_ALLOW_PTR
293309
/** generates a token list from the given buffer */
294-
TokenList(View data, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
295-
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, outputList, 0)
310+
TokenList(View data, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
311+
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, dui, outputList, 0)
296312
{}
297313
#ifdef __cpp_lib_span
298314
/** generates a token list from the given buffer */
299-
TokenList(std::span<const char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
300-
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, outputList, 0)
315+
TokenList(std::span<const char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
316+
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, dui, outputList, 0)
301317
{}
302318

303319
/** generates a token list from the given buffer */
304-
TokenList(std::span<const unsigned char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
305-
: TokenList(data.data(), data.size(), filenames, filename, outputList, 0)
320+
TokenList(std::span<const unsigned char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
321+
: TokenList(data.data(), data.size(), filenames, filename, dui, outputList, 0)
306322
{}
307323
#endif // __cpp_lib_span
308324

309325
/** generates a token list from the given filename parameter */
310-
TokenList(const std::string &filename, std::vector<std::string> &filenames, OutputList *outputList = nullptr);
326+
TokenList(const std::string &filename, std::vector<std::string> &filenames, const DUI &dui = {}, OutputList *outputList = nullptr);
311327
TokenList(const TokenList &other);
312328
TokenList(TokenList &&other);
313329
~TokenList();
@@ -323,7 +339,7 @@ namespace simplecpp {
323339
void dump(bool linenrs = false) const;
324340
std::string stringify(bool linenrs = false) const;
325341

326-
void readfile(Stream &stream, const std::string &filename=std::string(), OutputList *outputList = nullptr);
342+
void readfile(Stream &stream, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr);
327343
/**
328344
* @throws std::overflow_error thrown on overflow or division by zero
329345
* @throws std::runtime_error thrown on invalid expressions
@@ -387,7 +403,7 @@ namespace simplecpp {
387403
const std::string& file(const Location& loc) const;
388404

389405
private:
390-
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList, int /*unused*/);
406+
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename, const DUI &dui, OutputList *outputList, int /*unused*/);
391407

392408
void combineOperators();
393409

@@ -436,21 +452,6 @@ namespace simplecpp {
436452
long long result; // condition result
437453
};
438454

439-
/**
440-
* Command line preprocessor settings.
441-
* On the command line these are configured by -D, -U, -I, --include, -std
442-
*/
443-
struct SIMPLECPP_LIB DUI {
444-
DUI() = default;
445-
std::list<std::string> defines;
446-
std::set<std::string> undefined;
447-
std::list<std::string> includePaths;
448-
std::list<std::string> includes;
449-
std::string std;
450-
bool clearIncludeCache{};
451-
bool removeComments{}; /** remove comment tokens from included files */
452-
};
453-
454455
struct SIMPLECPP_LIB FileData {
455456
/** The canonical filename associated with this data */
456457
std::string filename;
@@ -589,9 +590,15 @@ namespace simplecpp {
589590
/** Returns the C version a given standard */
590591
SIMPLECPP_LIB cstd_t getCStd(const std::string &std);
591592

593+
/** Returns the name of a C standard */
594+
SIMPLECPP_LIB const char *getCStdName(cstd_t std);
595+
592596
/** Returns the C++ version a given standard */
593597
SIMPLECPP_LIB cppstd_t getCppStd(const std::string &std);
594598

599+
/** Returns the name of a C++ standard */
600+
SIMPLECPP_LIB const char *getCppStdName(cppstd_t std);
601+
595602
/** Returns the __STDC_VERSION__ value for a given standard */
596603
SIMPLECPP_LIB std::string getCStdString(const std::string &std);
597604
SIMPLECPP_LIB std::string getCStdString(cstd_t std);

0 commit comments

Comments
 (0)