Fix ticket #315 (Segmentation fault when checking Linux kernel) (previous fix was only partial fix)

http://apps.sourceforge.net/trac/cppcheck/ticket/315
This commit is contained in:
Reijo Tomperi 2009-05-19 22:19:15 +03:00
parent d6d55c2db4
commit a3f469d339
3 changed files with 209 additions and 85 deletions

View File

@ -21,6 +21,7 @@
#include "preprocessor.h"
#include "tokenize.h"
#include "token.h"
#include "filelister.h"
#include <algorithm>
#include <stdexcept>
@ -29,8 +30,187 @@
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <vector>
void Preprocessor::writeError(const std::string &fileName, const std::string &code, size_t endPos, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText)
{
if (!errorLogger)
{
return;
}
// line number in parsed code
unsigned int lineno = 1;
// The current token being parsed
std::string CurrentToken;
// lineNumbers holds line numbers for files in fileIndexes
// every time an include file is complitely parsed, last item in the vector
// is removed and lineno is set to point to that value.
std::vector<unsigned int> lineNumbers;
// fileIndexes holds index for _files vector about currently parsed files
// every time an include file is complitely parsed, last item in the vector
// is removed and FileIndex is set to point to that value.
std::vector<std::string> fileIndexes;
// FileIndex. What file in the _files vector is read now?
std::string FileIndex = fileName;
// Read one byte at a time from code and create tokens
if (endPos > code.length())
endPos = code.length();
for (size_t codePos = 0; codePos < endPos; ++codePos)
{
char ch = code[codePos];
// We are not handling UTF and stuff like that. Code is supposed to plain simple text.
if (ch < 0)
continue;
// char/string..
if (ch == '\'' || ch == '\"')
{
std::string line;
// read char
bool special = false;
char c = ch;
do
{
// Append token..
line += c;
if (c == '\n')
++lineno;
// Special sequence '\.'
if (special)
special = false;
else
special = (c == '\\');
// Get next character
++codePos;
c = code[codePos];
}
while (codePos < endPos && (special || c != ch));
line += ch;
// Handle #file "file.h"
if (CurrentToken == "#file")
{
// Extract the filename
line = line.substr(1, line.length() - 2);
// Has this file been tokenized already?
++lineno;
fileIndexes.push_back(FileIndex);
FileIndex = FileLister::simplifyPath(line.c_str());
lineNumbers.push_back(lineno);
lineno = 0;
}
CurrentToken.clear();
continue;
}
if (strchr("+-*/%&|^?!=<>[](){};:,.~\n ", ch))
{
if (ch == '.' &&
CurrentToken.length() > 0 &&
std::isdigit(CurrentToken[0]))
{
// Don't separate doubles "5.4"
}
else if (strchr("+-", ch) &&
CurrentToken.length() > 0 &&
std::isdigit(CurrentToken[0]) &&
CurrentToken[CurrentToken.length()-1] == 'e')
{
// Don't separate doubles "4.2e+10"
}
else
{
if (CurrentToken == "#file")
{
// Handle this where strings are handled
continue;
}
else if (CurrentToken == "#endfile")
{
if (lineNumbers.empty() || fileIndexes.empty())
{
std::cerr << "####### Preprocessor bug! #######\n";
std::exit(0);
}
lineno = lineNumbers.back();
lineNumbers.pop_back();
FileIndex = fileIndexes.back();
fileIndexes.pop_back();
CurrentToken.clear();
continue;
}
// If token contains # characters, split it up
std::string temp;
for (std::string::size_type i = 0; i < CurrentToken.length(); ++i)
{
if (CurrentToken[i] == '#' && CurrentToken.length() + 1 > i && CurrentToken[i+1] == '#')
{
temp.clear();
++i;
}
else
temp += CurrentToken[i];
}
CurrentToken.clear();
if (ch == '\n')
{
++lineno;
continue;
}
else if (ch == ' ')
{
continue;
}
CurrentToken += ch;
// Add "++", "--" or ">>" token
if ((ch == '+' || ch == '-' || ch == '>') && (code[codePos+1] == ch))
{
++codePos;
CurrentToken += code[codePos];
}
CurrentToken.clear();
continue;
}
}
CurrentToken += ch;
}
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = lineno;
loc.file = FileIndex;
locationList.push_back(loc);
errorLogger->reportErr(
ErrorLogger::ErrorMessage(locationList,
"error",
errorText,
errorType));
}
Preprocessor::Preprocessor()
{
@ -881,51 +1061,13 @@ std::string Preprocessor::expandMacros(std::string code, const std::string &file
}
}
if (errorLogger)
{
// TODO, duplicate code. Refactor
std::string fname(filename);
int lineno = 0;
for (std::string::size_type p = pos1; p > 0; --p)
{
// newline..
if (code[p-1] == '\n')
lineno++;
writeError(filename,
code,
pos1,
errorLogger,
"noQuoteCharPair",
std::string("No pair for character (") + ch + "). Can't process file. File is either invalid or unicode, which is currently not supported.");
// #file..
else if (code[p-1] == '#')
{
// Previous char should be a newline..
if (p == 1 || code[p-2] == '\n')
{
// #file..
if (code.substr(p - 1, 6) == "#file ")
{
fname = code.substr(p + 5, code.find("\n", p) - p - 5);
break;
}
else
++lineno;
}
}
// start of file..
else if (p == 1)
++lineno;
}
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = lineno;
loc.file = fname;
locationList.push_back(loc);
errorLogger->reportErr(
ErrorLogger::ErrorMessage(locationList,
"error",
std::string("No pair for character (") + ch + "). Can't process file. File is either invalid or unicode, which is currently not supported.",
"noQuoteCharPair"));
}
return "";
}
@ -1031,50 +1173,16 @@ std::string Preprocessor::expandMacros(std::string code, const std::string &file
if (!macro.code(params, tempMacro))
{
// Syntax error in code
if (errorLogger)
{
std::string fname(filename);
int lineno = 1;
for (std::string::size_type p = pos1; p > 0; --p)
{
// newline..
if (code[p-1] == '\n')
lineno++;
// #file..
else if (code[p-1] == '#')
{
// Previous char should be a newline..
if (p == 1 || code[p-2] == '\n')
{
// #file..
if (code.substr(p - 1, 6) == "#file ")
{
fname = code.substr(p + 5, code.find("\n", p) - p - 5);
break;
}
else
++lineno;
}
}
writeError(filename,
code,
pos1,
errorLogger,
"syntaxError",
std::string("Syntax error. Not enough parameters for macro '") + macro.name() + "'.");
// start of file..
else if (p == 1)
++lineno;
}
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = lineno;
loc.file = fname;
locationList.push_back(loc);
errorLogger->reportErr(
ErrorLogger::ErrorMessage(locationList,
"error",
std::string("Syntax error. Not enough parameters for macro '") + macro.name() + "'.",
"syntaxError"));
}
return "";
}

View File

@ -76,6 +76,8 @@ public:
protected:
static void writeError(const std::string &fileName, const std::string &code, size_t pos, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText);
/**
* Replace "#if defined" with "#ifdef" where possible
*

View File

@ -913,7 +913,7 @@ private:
}
{
const char filedata[] = "#file abc.h\n"
const char filedata[] = "#file \"abc.h\"\n"
"#define a\n"
"\"\n"
"#endfile\n";
@ -926,6 +926,20 @@ private:
ASSERT_EQUALS("[abc.h:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str());
}
{
const char filedata[] = "#file \"abc.h\"\n"
"#define a\n"
"#endfile\n"
"\"\n";
// expand macros..
errout.str("");
const std::string actual(OurPreprocessor::expandMacros(filedata, this));
ASSERT_EQUALS("", actual);
ASSERT_EQUALS("[file.cpp:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str());
}
{
const char filedata[] = "#define A 1\n"
"#define B \"\n"