Fix ticket #306 (Invalid multi-line comment produces cryptic internal error)

http://apps.sourceforge.net/trac/cppcheck/ticket/306
This commit is contained in:
Reijo Tomperi 2009-05-13 22:18:02 +03:00
parent 997a784bb6
commit e5e82274dc
3 changed files with 147 additions and 79 deletions

View File

@ -71,9 +71,6 @@ std::string Preprocessor::read(std::istream &istr)
std::ostringstream code; std::ostringstream code;
for (char ch = readChar(istr); istr.good(); ch = readChar(istr)) for (char ch = readChar(istr); istr.good(); ch = readChar(istr))
{ {
if (ch < 0)
throw std::runtime_error("The code contains characters that are unhandled");
if (ch == '\n') if (ch == '\n')
++lineno; ++lineno;
@ -84,7 +81,7 @@ std::string Preprocessor::read(std::istream &istr)
// Skip spaces after ' ' and after '#' // Skip spaces after ' ' and after '#'
if (ch == ' ' && ignoreSpace) if (ch == ' ' && ignoreSpace)
continue; continue;
ignoreSpace = bool(ch == ' ' || ch == '#' || ch == '/'); ignoreSpace = bool(ch == ' ' || ch == '#' || ch == '/' || ch == '\n');
if (needSpace) if (needSpace)
{ {
@ -96,68 +93,8 @@ std::string Preprocessor::read(std::istream &istr)
if (ch == '#') if (ch == '#')
needSpace = true; needSpace = true;
// Remove comments..
if (ch == '/')
{
char chNext = readChar(istr);
if (chNext == '/')
{
while (istr.good() && ch != '\n')
ch = readChar(istr);
code << "\n";
++lineno;
}
else if (chNext == '*')
{
char chPrev = 0;
while (istr.good() && (chPrev != '*' || ch != '/'))
{
chPrev = ch;
ch = readChar(istr);
if (ch == '\n')
{
code << "\n";
++lineno;
}
}
}
else
{
if (chNext == '\n')
++lineno;
code << std::string(1, ch) << std::string(1, chNext);
}
}
// String or char constants..
else if (ch == '\"' || ch == '\'')
{
code << std::string(1, ch);
char chNext;
do
{
chNext = (char)istr.get();
if (chNext == '\\')
{
char chSeq = readChar(istr);
if (chSeq == '\n')
++newlines;
else
{
code << std::string(1, chNext);
code << std::string(1, chSeq);
}
}
else
code << std::string(1, chNext);
}
while (istr.good() && chNext != ch);
}
// <backspace><newline>.. // <backspace><newline>..
else if (ch == '\\') if (ch == '\\')
{ {
char chNext = 0; char chNext = 0;
while (true) while (true)
@ -197,6 +134,111 @@ std::string Preprocessor::read(std::istream &istr)
} }
} }
return removeComments(code.str());
}
std::string Preprocessor::removeComments(const std::string &str)
{
// For the error report
int lineno = 1;
// handling <backspace><newline>
// when this is encountered the <backspace><newline> will be "skipped".
// on the next <newline>, extra newlines will be added
unsigned int newlines = 0;
std::ostringstream code;
for (std::string::size_type i = 0; i < str.length(); ++i)
{
char ch = str[i];
if (ch < 0)
throw std::runtime_error("The code contains characters that are unhandled");
// Remove comments..
if (ch == '/')
{
++i;
char chNext = str[i];
if (chNext == '/')
{
while (i < str.length() && ch != '\n')
{
++i;
ch = str[i];
}
code << "\n";
++lineno;
}
else if (chNext == '*')
{
char chPrev = 0;
while (i < str.length() && (chPrev != '*' || ch != '/'))
{
chPrev = ch;
++i;
ch = str[i];
if (ch == '\n')
{
code << "\n";
++lineno;
}
}
}
else
{
if (chNext == '\n')
++lineno;
code << std::string(1, ch) << std::string(1, chNext);
}
}
// String or char constants..
else if (ch == '\"' || ch == '\'')
{
code << std::string(1, ch);
char chNext;
do
{
++i;
chNext = str[i];
if (chNext == '\\')
{
++i;
char chSeq = str[i];
if (chSeq == '\n')
++newlines;
else
{
code << std::string(1, chNext);
code << std::string(1, chSeq);
}
}
else
code << std::string(1, chNext);
}
while (i < str.length() && chNext != ch);
}
// Just some code..
else
{
code << std::string(1, ch);
// if there has been <backspace><newline> sequences, add extra newlines..
if (ch == '\n' && newlines > 0)
{
code << std::string(newlines, '\n');
newlines = 0;
}
}
}
return code.str(); return code.str();
} }

View File

@ -86,6 +86,13 @@ protected:
static std::string expandMacros(std::string code, const std::string &filename, ErrorLogger *errorLogger); static std::string expandMacros(std::string code, const std::string &filename, ErrorLogger *errorLogger);
/**
* Remove comments from code. This should only be called from read().
* @param str Code processed by read().
* @return code without comments
*/
static std::string removeComments(const std::string &str);
private: private:
/** /**

View File

@ -234,7 +234,7 @@ private:
preprocessor.preprocess(istr, actual, "file.c"); preprocessor.preprocess(istr, actual, "file.c");
// Compare results.. // Compare results..
ASSERT_EQUALS("\n\" # ifdef WIN32\"\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\" #ifdef WIN32\"\n\n\n\n", actual[""]);
ASSERT_EQUALS("\n\n\nqwerty\n\n", actual["WIN32"]); ASSERT_EQUALS("\n\n\nqwerty\n\n", actual["WIN32"]);
ASSERT_EQUALS(2, static_cast<unsigned int>(actual.size())); ASSERT_EQUALS(2, static_cast<unsigned int>(actual.size()));
} }
@ -383,20 +383,39 @@ private:
void comments1() void comments1()
{ {
const char filedata[] = "/*\n" {
"#ifdef WIN32\n" const char filedata[] = "/*\n"
"#endif\n" "#ifdef WIN32\n"
"*/\n"; "#endif\n"
"*/\n";
// Preprocess => actual result.. // Preprocess => actual result..
std::istringstream istr(filedata); std::istringstream istr(filedata);
std::map<std::string, std::string> actual; std::map<std::string, std::string> actual;
Preprocessor preprocessor; Preprocessor preprocessor;
preprocessor.preprocess(istr, actual, "file.c"); preprocessor.preprocess(istr, actual, "file.c");
// Compare results.. // Compare results..
ASSERT_EQUALS("\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\n\n\n", actual[""]);
ASSERT_EQUALS(1, static_cast<unsigned int>(actual.size())); ASSERT_EQUALS(1, static_cast<unsigned int>(actual.size()));
}
{
const char filedata[] = "/*\n"
"\x080 #ifdef WIN32\n"
"#endif\n"
"*/\n";
// Preprocess => actual result..
std::istringstream istr(filedata);
std::map<std::string, std::string> actual;
Preprocessor preprocessor;
preprocessor.preprocess(istr, actual, "file.c");
// Compare results..
ASSERT_EQUALS("\n\n\n\n", actual[""]);
ASSERT_EQUALS(1, static_cast<unsigned int>(actual.size()));
}
} }
@ -944,7 +963,7 @@ private:
// Compare results.. // Compare results..
ASSERT_EQUALS(1, static_cast<unsigned int>(actual.size())); ASSERT_EQUALS(1, static_cast<unsigned int>(actual.size()));
TODO_ASSERT_EQUALS("\n\nvoid f() { }\n", actual[""]); ASSERT_EQUALS("\n\nvoid f() { }\n", actual[""]);
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }