CheckClass; Added files (Check for mistakes related to classes)
This commit is contained in:
parent
399a3e2598
commit
f20c34a751
|
@ -0,0 +1,473 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#include "CheckClass.h"
|
||||
#include "Tokenize.h"
|
||||
#include "CommonCheck.h"
|
||||
#include <locale>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
extern bool IsName(const char str[]);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
struct VAR
|
||||
{
|
||||
bool is_class;
|
||||
const char *name;
|
||||
bool init;
|
||||
bool is_pointer;
|
||||
struct VAR *next;
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static struct VAR *ClassChecking_GetVarList(const char classname[])
|
||||
{
|
||||
// Locate class..
|
||||
const char *pattern[] = {"class","","{",0};
|
||||
pattern[1] = classname;
|
||||
TOKEN *tok1 = findtoken(tokens, pattern);
|
||||
|
||||
// All classes..
|
||||
struct _class
|
||||
{
|
||||
const char *name;
|
||||
struct _class *next;
|
||||
};
|
||||
struct _class *classes = NULL;
|
||||
const char *pattern_anyclass[] = {"class","",NULL};
|
||||
for (TOKEN *t = findtoken(tokens,pattern_anyclass); t; t = findtoken(t->next,pattern_anyclass))
|
||||
{
|
||||
_class *newclass = new _class;
|
||||
newclass->name = t->next->str;
|
||||
newclass->next = classes;
|
||||
classes = newclass;
|
||||
}
|
||||
|
||||
// Get variable list..
|
||||
bool is_class = false;
|
||||
bool is_pointer = false;
|
||||
struct VAR *varlist = NULL;
|
||||
unsigned int indentlevel = 0;
|
||||
for (TOKEN *tok = tok1; tok; tok = tok->next)
|
||||
{
|
||||
if (!tok->next)
|
||||
break;
|
||||
|
||||
if (tok->str[0] == '{')
|
||||
indentlevel++;
|
||||
if (tok->str[0] == '}')
|
||||
{
|
||||
if (indentlevel <= 1)
|
||||
break;
|
||||
indentlevel--;
|
||||
}
|
||||
|
||||
if (strchr(";{}", tok->str[0]))
|
||||
is_class = is_pointer = false;
|
||||
else if (IsName(tok->str))
|
||||
{
|
||||
for (_class *c = classes; c; c = c->next)
|
||||
is_class |= (strcmp(c->name, tok->str) == 0);
|
||||
}
|
||||
|
||||
if (tok->str[0] == '*')
|
||||
is_pointer = true;
|
||||
|
||||
// Member variable?
|
||||
if ((indentlevel == 1) &&
|
||||
(tok->next->str[0] == ';') &&
|
||||
(IsName(tok->str)) &&
|
||||
(strcmp(tok->str,"const") != 0 ))
|
||||
{
|
||||
struct VAR *var = new VAR;
|
||||
memset(var, 0, sizeof(struct VAR));
|
||||
var->name = tok->str;
|
||||
var->next = varlist;
|
||||
var->is_class = is_class;
|
||||
var->is_pointer = is_pointer;
|
||||
varlist = var;
|
||||
}
|
||||
}
|
||||
|
||||
while (classes)
|
||||
{
|
||||
_class *next = classes->next;
|
||||
delete classes;
|
||||
classes = next;
|
||||
}
|
||||
|
||||
return varlist;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static TOKEN * ClassChecking_VarList_RemoveAssigned(TOKEN *_tokens, struct VAR *varlist, const char classname[], const char funcname[])
|
||||
{
|
||||
// Locate class member function
|
||||
const char *pattern[] = {"","::","","(",NULL};
|
||||
pattern[0] = classname;
|
||||
pattern[2] = funcname;
|
||||
|
||||
// Locate member function implementation..
|
||||
TOKEN *ftok = findtoken(_tokens, pattern);
|
||||
if (!ftok)
|
||||
return NULL;
|
||||
|
||||
bool BeginLine = false;
|
||||
bool Assign = false;
|
||||
unsigned int indentlevel = 0;
|
||||
for (; ftok; ftok = ftok->next)
|
||||
{
|
||||
if (!ftok->next)
|
||||
break;
|
||||
|
||||
// Class constructor.. initializing variables like this
|
||||
// clKalle::clKalle() : var(value) { }
|
||||
if (indentlevel==0 && strcmp(classname,funcname)==0)
|
||||
{
|
||||
if (Assign &&
|
||||
IsName(ftok->str) &&
|
||||
ftok->next->str[0]=='(')
|
||||
{
|
||||
for (struct VAR *var = varlist; var; var = var->next)
|
||||
{
|
||||
if (strcmp(var->name,ftok->str))
|
||||
continue;
|
||||
var->init = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assign |= (ftok->str[0] == ':');
|
||||
}
|
||||
|
||||
|
||||
if (ftok->str[0] == '{')
|
||||
{
|
||||
indentlevel++;
|
||||
Assign = false;
|
||||
}
|
||||
|
||||
if (ftok->str[0] == '}')
|
||||
{
|
||||
if (indentlevel <= 1)
|
||||
break;
|
||||
indentlevel--;
|
||||
}
|
||||
|
||||
if (BeginLine && indentlevel>=1 && IsName(ftok->str))
|
||||
{
|
||||
// Clearing all variables..
|
||||
if (match(ftok,"memset ( this ,"))
|
||||
{
|
||||
for (struct VAR *var = varlist; var; var = var->next)
|
||||
var->init = true;
|
||||
}
|
||||
|
||||
// Calling member function?
|
||||
if (ftok->next->str[0] == '(')
|
||||
ClassChecking_VarList_RemoveAssigned(tokens, varlist, classname, ftok->str);
|
||||
|
||||
// Assignment of member variable?
|
||||
if (strcmp(ftok->next->str, "=") == 0)
|
||||
{
|
||||
for (struct VAR *var = varlist; var; var = var->next)
|
||||
{
|
||||
if (strcmp(var->name,ftok->str))
|
||||
continue;
|
||||
var->init = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calling member function..
|
||||
if (strcmp(ftok->next->str,".")==0 || strcmp(ftok->next->str,"->")==0)
|
||||
{
|
||||
// The functions 'clear' and 'Clear' are supposed to initialize variable.
|
||||
if (stricmp(ftok->next->next->str,"clear") == 0)
|
||||
{
|
||||
for (struct VAR *var = varlist; var; var = var->next)
|
||||
{
|
||||
if (strcmp(var->name,ftok->str))
|
||||
continue;
|
||||
var->init = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BeginLine = (strchr("{};", ftok->str[0]));
|
||||
}
|
||||
|
||||
return ftok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// ClassCheck: Check that all class constructors are ok.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void CheckConstructors()
|
||||
{
|
||||
// Locate class
|
||||
const char *pattern_classname[] = {"class","","{",NULL};
|
||||
TOKEN *tok1 = findtoken(tokens, pattern_classname);
|
||||
while (tok1)
|
||||
{
|
||||
const char *classname = tok1->next->str;
|
||||
|
||||
// Are there a class constructor?
|
||||
const char *pattern_constructor[] = {"clKalle","::","clKalle","(",NULL};
|
||||
pattern_constructor[0] = classname;
|
||||
pattern_constructor[2] = classname;
|
||||
if (!findtoken(tokens,pattern_constructor))
|
||||
{
|
||||
// There's no class implementation, it must be somewhere else.
|
||||
tok1 = findtoken( tok1->next, pattern_classname );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that all member variables are initialized..
|
||||
struct VAR *varlist = ClassChecking_GetVarList(classname);
|
||||
ClassChecking_VarList_RemoveAssigned(tokens, varlist, classname, classname);
|
||||
|
||||
// Check if any variables are uninitialized
|
||||
for (struct VAR *var = varlist; var; var = var->next)
|
||||
{
|
||||
if (!var->init && (var->is_pointer || !var->is_class))
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
ostr << "Uninitialized member variable '" << classname << "::" << var->name << "'";
|
||||
ReportErr(ostr.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the varlist..
|
||||
while (varlist)
|
||||
{
|
||||
struct VAR *nextvar = varlist->next;
|
||||
delete varlist;
|
||||
varlist = nextvar;
|
||||
}
|
||||
|
||||
tok1 = findtoken( tok1->next, pattern_classname );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// ClassCheck: Unused private functions
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void CheckUnusedPrivateFunctions()
|
||||
{
|
||||
// Locate some class
|
||||
const char *pattern_class[] = {"class","","{",NULL};
|
||||
for (TOKEN *tok1 = findtoken(tokens, pattern_class); tok1; tok1 = findtoken(tok1->next, pattern_class))
|
||||
{
|
||||
const char *classname = tok1->next->str;
|
||||
|
||||
// The class implementation must be available..
|
||||
const char *pattern_classconstructor[] = {"","::","",NULL};
|
||||
pattern_classconstructor[0] = classname;
|
||||
pattern_classconstructor[2] = classname;
|
||||
if (!findtoken(tokens,pattern_classconstructor))
|
||||
continue;
|
||||
|
||||
// Get private functions..
|
||||
std::list<std::string> FuncList;
|
||||
FuncList.clear();
|
||||
bool priv = false;
|
||||
unsigned int indent_level = 0;
|
||||
for (TOKEN *tok = tok1; tok; tok = tok->next)
|
||||
{
|
||||
if (match(tok,"friend class"))
|
||||
{
|
||||
// Todo: Handle friend classes
|
||||
FuncList.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (tok->str[0] == '{')
|
||||
indent_level++;
|
||||
if (tok->str[0] == '}')
|
||||
{
|
||||
if (indent_level <= 1)
|
||||
break;
|
||||
indent_level--;
|
||||
}
|
||||
if (strcmp(tok->str,"};") == 0)
|
||||
break;
|
||||
if (strcmp(tok->str,"private:") == 0)
|
||||
priv = true;
|
||||
else if (strcmp(tok->str,"public:") == 0)
|
||||
priv = false;
|
||||
else if (strcmp(tok->str,"protected:") == 0)
|
||||
priv = false;
|
||||
else if (priv && indent_level == 1)
|
||||
{
|
||||
if (std::isalpha(tok->str[0]) &&
|
||||
tok->next->str[0]=='(' &&
|
||||
strcmp(tok->str,classname) != 0)
|
||||
{
|
||||
FuncList.push_back(tok->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all private functions are used..
|
||||
const char *pattern_function[] = {"","::",NULL};
|
||||
pattern_function[0] = classname;
|
||||
bool HasFuncImpl = false;
|
||||
for (TOKEN *ftok = findtoken(tokens, pattern_function); ftok; ftok = findtoken(ftok->next,pattern_function))
|
||||
{
|
||||
int numpar = 0;
|
||||
while (ftok && ftok->str[0]!=';' && ftok->str[0]!='{')
|
||||
{
|
||||
if (ftok->str[0] == '(')
|
||||
numpar++;
|
||||
else if (ftok->str[0] == ')')
|
||||
numpar--;
|
||||
ftok = ftok->next;
|
||||
}
|
||||
|
||||
if (!ftok)
|
||||
break;
|
||||
|
||||
if (ftok->str[0] == ';')
|
||||
continue;
|
||||
|
||||
if (numpar != 0)
|
||||
continue;
|
||||
|
||||
HasFuncImpl = true;
|
||||
|
||||
indent_level = 0;
|
||||
while (ftok)
|
||||
{
|
||||
if (ftok->str[0] == '{')
|
||||
indent_level++;
|
||||
if (ftok->str[0] == '}')
|
||||
{
|
||||
if (indent_level<=1)
|
||||
break;
|
||||
indent_level--;
|
||||
}
|
||||
if (ftok->next && ftok->next->str[0] == '(')
|
||||
FuncList.remove(ftok->str);
|
||||
ftok = ftok->next;
|
||||
}
|
||||
}
|
||||
|
||||
while (HasFuncImpl && !FuncList.empty())
|
||||
{
|
||||
bool fp = false;
|
||||
|
||||
// Final check; check if the function pointer is used somewhere..
|
||||
const char *_pattern[] = {"=","",NULL};
|
||||
_pattern[1] = FuncList.front().c_str();
|
||||
fp |= (findtoken(tokens, _pattern) != NULL);
|
||||
_pattern[0] = "(";
|
||||
fp |= (findtoken(tokens, _pattern) != NULL);
|
||||
_pattern[0] = ")";
|
||||
fp |= (findtoken(tokens, _pattern) != NULL);
|
||||
_pattern[0] = ",";
|
||||
fp |= (findtoken(tokens, _pattern) != NULL);
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
ostr << "Class '" << classname << "', unused private function: '" << FuncList.front() << "'";
|
||||
ReportErr(ostr.str());
|
||||
}
|
||||
FuncList.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// ClassCheck: Check that memset is not used on classes
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void CheckMemset()
|
||||
{
|
||||
// Locate all 'memset' tokens..
|
||||
for (TOKEN *tok = tokens; tok; tok = tok->next)
|
||||
{
|
||||
if (strcmp(tok->str,"memset")!=0)
|
||||
continue;
|
||||
|
||||
const char *type = NULL;
|
||||
if (match(tok, "memset ( var , num , sizeof ( type ) )"))
|
||||
type = getstr(tok, 8);
|
||||
else if (match(tok, "memset ( & var , num , sizeof ( type ) )"))
|
||||
type = getstr(tok, 9);
|
||||
else if (match(tok, "memset ( var , num , sizeof ( struct type ) )"))
|
||||
type = getstr(tok, 9);
|
||||
else if (match(tok, "memset ( & var , num , sizeof ( struct type ) )"))
|
||||
type = getstr(tok, 10);
|
||||
|
||||
// No type defined => The tokens didn't match
|
||||
if (!(type && type[0]))
|
||||
continue;
|
||||
|
||||
// It will be assumed that memset can be used upon 'this'.
|
||||
// Todo: Check this too
|
||||
if (strcmp(getstr(tok,2),"this") == 0)
|
||||
continue;
|
||||
|
||||
// Warn if type is a class..
|
||||
const char *pattern1[] = {"class","",NULL};
|
||||
pattern1[1] = type;
|
||||
if (findtoken(tokens,pattern1))
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
ostr << FileLine(tok) << ": Using 'memset' on class.";
|
||||
ReportErr(ostr.str());
|
||||
}
|
||||
|
||||
// Warn if type is a struct that contains any std::*
|
||||
const char *pattern2[] = {"struct","","{",NULL};
|
||||
pattern2[1] = type;
|
||||
for (TOKEN *tstruct = findtoken(tokens, pattern2); tstruct; tstruct = tstruct->next)
|
||||
{
|
||||
if (tstruct->str[0] == '}')
|
||||
break;
|
||||
|
||||
if (match(tstruct, "std :: type var ;"))
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
ostr << FileLine(tok) << ": Using 'memset' on struct that contains a 'std::" << getstr(tstruct,2) << "'";
|
||||
ReportErr(ostr.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// ClassCheck: "void operator=("
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void CheckOperatorEq1()
|
||||
{
|
||||
const char *pattern[] = {"void", "operator", "=", "(", NULL};
|
||||
if (TOKEN *tok = findtoken(tokens,pattern))
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
ostr << FileLine(tok) << ": 'operator=' should return something";
|
||||
ReportErr(ostr.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#ifndef CheckClassH
|
||||
#define CheckClassH
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
void CheckConstructors();
|
||||
|
||||
void CheckUnusedPrivateFunctions();
|
||||
|
||||
void CheckMemset();
|
||||
|
||||
void CheckOperatorEq1(); // Warning upon "void operator=(.."
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#ifndef CommonCheckH
|
||||
#define CommonCheckH
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <string>
|
||||
|
||||
struct TOKEN;
|
||||
|
||||
std::string FileLine(TOKEN *tok);
|
||||
|
||||
void ReportErr(const std::string errmsg);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
|
@ -0,0 +1,453 @@
|
|||
|
||||
#include "Statements.h"
|
||||
#include "tokenize.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
std::vector<std::string> VariableNames;
|
||||
std::list<STATEMENT> Statements;
|
||||
|
||||
extern bool Debug;
|
||||
|
||||
extern bool IsName(const char str[]);
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Create statement list
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void AppendStatement(STATEMENT::etype Type, TOKEN *tok, std::string Var="")
|
||||
{
|
||||
STATEMENT NewStatement;
|
||||
NewStatement.Type = Type;
|
||||
NewStatement.Token = tok;
|
||||
if (Var.empty())
|
||||
{
|
||||
NewStatement.VarIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool Found = false;
|
||||
for (unsigned int i = 0; i < VariableNames.size(); i++)
|
||||
{
|
||||
if (VariableNames[i] == Var)
|
||||
{
|
||||
Found = true;
|
||||
NewStatement.VarIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! Found )
|
||||
{
|
||||
NewStatement.VarIndex = VariableNames.size();
|
||||
VariableNames.push_back(Var);
|
||||
}
|
||||
}
|
||||
Statements.push_back(NewStatement);
|
||||
}
|
||||
|
||||
TOKEN *GotoNextStatement(TOKEN *tok)
|
||||
{
|
||||
if (tok && (tok->str[0]=='{' || tok->str[0]=='}'))
|
||||
return tok->next;
|
||||
|
||||
if (tok)
|
||||
tok = tok->next;
|
||||
int parlevel = 0;
|
||||
for (; tok; tok = tok->next)
|
||||
{
|
||||
if (tok->str[0] == '(')
|
||||
parlevel++;
|
||||
else if (tok->str[0] == ')')
|
||||
parlevel--;
|
||||
if (strchr("{}", tok->str[0]))
|
||||
break;
|
||||
if (parlevel==0 && tok->str[0] == ';')
|
||||
{
|
||||
while (tok && tok->str[0] == ';')
|
||||
tok = tok->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
|
||||
|
||||
void GetVariableName(TOKEN * &Token, std::string &varname)
|
||||
{
|
||||
varname = "";
|
||||
|
||||
if (Token == NULL)
|
||||
return;
|
||||
|
||||
if (!IsName(Token->str))
|
||||
return;
|
||||
|
||||
// Get variable name..
|
||||
std::ostringstream ostr;
|
||||
bool array = false;
|
||||
bool name = false;
|
||||
for (TOKEN *tok = Token; tok; tok = tok->next)
|
||||
{
|
||||
const char *str = tok->str;
|
||||
|
||||
if ((array) || (str[0]=='['))
|
||||
{
|
||||
ostr << str;
|
||||
array = (str[0] != ']');
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (name && IsName(str))
|
||||
return;
|
||||
name = IsName(str);
|
||||
if (!name && strcmp(str,".") && strcmp(str,"->") && strcmp(str,"::"))
|
||||
{
|
||||
if (str[0] == '(')
|
||||
return;
|
||||
Token = tok;
|
||||
break;
|
||||
}
|
||||
ostr << str;
|
||||
}
|
||||
}
|
||||
|
||||
varname = ostr.str();
|
||||
}
|
||||
|
||||
|
||||
void CreateStatementList()
|
||||
{
|
||||
// Clear lists..
|
||||
VariableNames.clear();
|
||||
Statements.clear();
|
||||
|
||||
int indentlevel = 0;
|
||||
for (TOKEN *tok = tokens; tok; tok = GotoNextStatement(tok))
|
||||
{
|
||||
if (tok->str[0] == '{')
|
||||
{
|
||||
AppendStatement(STATEMENT::OBRACE, tok);
|
||||
indentlevel++;
|
||||
}
|
||||
else if (tok->str[0] == '}')
|
||||
{
|
||||
AppendStatement(STATEMENT::EBRACE, tok);
|
||||
indentlevel--;
|
||||
}
|
||||
else if (indentlevel >= 1)
|
||||
{
|
||||
bool hasif = false, hasloop = false, hasswitch = false;
|
||||
|
||||
if (strcmp(tok->str,"if")==0)
|
||||
{
|
||||
hasif = true;
|
||||
AppendStatement(STATEMENT::IF, tok);
|
||||
}
|
||||
else if (strcmp(tok->str,"else")==0)
|
||||
{
|
||||
hasif = true;
|
||||
if (strcmp(getstr(tok,1),"if")==0)
|
||||
AppendStatement(STATEMENT::ELSEIF, tok);
|
||||
else
|
||||
AppendStatement(STATEMENT::ELSE, tok);
|
||||
}
|
||||
|
||||
else if (strcmp(tok->str,"do")==0 ||
|
||||
strcmp(tok->str,"while")==0 ||
|
||||
strcmp(tok->str,"for")==0)
|
||||
{
|
||||
AppendStatement(STATEMENT::LOOP, tok);
|
||||
hasloop = true;
|
||||
}
|
||||
|
||||
else if (strcmp(tok->str,"switch")==0)
|
||||
{
|
||||
AppendStatement(STATEMENT::SWITCH, tok);
|
||||
hasswitch = true;
|
||||
}
|
||||
|
||||
// Declaring variables..
|
||||
if (IsName(tok->str) && strcmp(tok->str,"delete") && strcmp(tok->str,"return"))
|
||||
{
|
||||
const char *str1 = getstr(tok, 1);
|
||||
bool decl = IsName(str1) || str1[0]=='*';
|
||||
for (TOKEN *tok2 = decl ? tok->next : NULL; tok2; tok2 = tok2->next)
|
||||
{
|
||||
if (strchr("{};.", tok2->str[0]))
|
||||
break;
|
||||
|
||||
const char *str1 = getstr(tok2, 1);
|
||||
if (IsName(tok2->str) && strchr("[=,;",str1[0]))
|
||||
{
|
||||
AppendStatement(STATEMENT::DECL, tok2, tok2->str);
|
||||
while (tok2 && !strchr(",;", tok2->str[0]))
|
||||
tok2 = tok2->next;
|
||||
if (tok2->str[0] == ';')
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Assign..
|
||||
for (TOKEN *tok2 = tok; tok2; tok2 = tok2->next)
|
||||
{
|
||||
if (strchr("{};", tok2->str[0]))
|
||||
break;
|
||||
|
||||
TOKEN *eq = tok2;
|
||||
std::string varname = "";
|
||||
GetVariableName(eq, varname);
|
||||
|
||||
// Equal with..
|
||||
if (eq && strcmp(eq->str,"=")==0 && !varname.empty())
|
||||
{
|
||||
TOKEN *rs = eq->next;
|
||||
|
||||
bool ismalloc = false;
|
||||
ismalloc |= match(rs, "strdup (");
|
||||
if (rs->str[0]=='(' && IsName(getstr(rs,1)))
|
||||
{
|
||||
ismalloc |= match(rs, "( type * ) malloc (");
|
||||
ismalloc |= match(rs, "( type * * ) malloc (");
|
||||
ismalloc |= match(rs, "( type type * ) malloc (");
|
||||
ismalloc |= match(rs, "( type type * * ) malloc (");
|
||||
}
|
||||
|
||||
if ( ismalloc )
|
||||
AppendStatement(STATEMENT::MALLOC, tok2, varname);
|
||||
|
||||
else if ( match(rs,"new type ;") )
|
||||
AppendStatement(STATEMENT::NEW, tok2, varname);
|
||||
|
||||
else if ( match(rs, "new type (") )
|
||||
AppendStatement(STATEMENT::NEW, tok2, varname);
|
||||
|
||||
else if ( match(rs, "new type [") )
|
||||
AppendStatement(STATEMENT::NEWARRAY, tok2, varname);
|
||||
|
||||
else
|
||||
AppendStatement(STATEMENT::ASSIGN, tok2, varname);
|
||||
|
||||
tok2 = eq;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete..
|
||||
for (TOKEN *tok2 = tok; tok2; tok2 = tok2->next)
|
||||
{
|
||||
if (strchr("{};", tok2->str[0]))
|
||||
break;
|
||||
|
||||
if (match(tok2, "free ( var ) ;"))
|
||||
AppendStatement(STATEMENT::FREE, tok2, getstr(tok2, 2));
|
||||
|
||||
if (match(tok2, "delete var ;"))
|
||||
AppendStatement(STATEMENT::DELETE, tok2, getstr(tok2,1));
|
||||
|
||||
if (match(tok2, "delete [ ] var ;"))
|
||||
AppendStatement(STATEMENT::DELETEARRAY, tok2, getstr(tok2,3));
|
||||
}
|
||||
|
||||
// Use..
|
||||
bool UseVar = false;
|
||||
int parlevel = 0;
|
||||
for (TOKEN *tok2 = tok; tok2; tok2 = tok2->next)
|
||||
{
|
||||
if (parlevel==0 && strchr("{};", tok2->str[0]))
|
||||
break;
|
||||
|
||||
if (tok2->str[0] == '(')
|
||||
parlevel++;
|
||||
if (tok2->str[0] == ')')
|
||||
parlevel--;
|
||||
if (parlevel == 0 && tok2->str[0] == ',')
|
||||
UseVar = false;
|
||||
|
||||
else if (tok2->str[0]=='=' || tok2->str[0]=='(')
|
||||
UseVar = true;
|
||||
|
||||
else if (UseVar && IsName(tok2->str))
|
||||
{
|
||||
std::string varname = "";
|
||||
GetVariableName(tok2, varname);
|
||||
|
||||
if (!varname.empty() &&
|
||||
varname!="continue" &&
|
||||
varname!="break" &&
|
||||
varname!="return")
|
||||
{
|
||||
AppendStatement(STATEMENT::USE, tok2, varname);
|
||||
}
|
||||
|
||||
if (tok2->str[0] == ')')
|
||||
parlevel--;
|
||||
|
||||
if (parlevel==0 && tok2->str[0]==';')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return, continue, break..
|
||||
for (TOKEN *tok2 = tok; tok2; tok2 = tok2->next)
|
||||
{
|
||||
if (strchr("{};", tok2->str[0]))
|
||||
break;
|
||||
|
||||
if (strcmp(tok2->str,"continue")==0)
|
||||
{
|
||||
AppendStatement(STATEMENT::CONTINUE, tok2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(tok2->str,"break")==0)
|
||||
{
|
||||
AppendStatement(STATEMENT::BREAK, tok2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(tok2->str,"return")==0)
|
||||
{
|
||||
if (IsName(getstr(tok2,1)) && strcmp(getstr(tok2,2),";")==0)
|
||||
AppendStatement(STATEMENT::RETURN, tok2, getstr(tok2,1));
|
||||
|
||||
else
|
||||
AppendStatement(STATEMENT::RETURN, tok2, ";");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasif)
|
||||
{
|
||||
AppendStatement(STATEMENT::ENDIF, tok);
|
||||
}
|
||||
else if (hasloop)
|
||||
{
|
||||
AppendStatement(STATEMENT::ENDLOOP, tok);
|
||||
}
|
||||
else if (hasswitch)
|
||||
{
|
||||
AppendStatement(STATEMENT::ENDSWITCH, tok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Debug)
|
||||
{
|
||||
std::list<STATEMENT>::const_iterator it;
|
||||
for (it = Statements.begin(); it != Statements.end(); it++)
|
||||
{
|
||||
STATEMENT s = *it;
|
||||
std::cout << it->Token->linenr << " : ";
|
||||
switch (s.Type)
|
||||
{
|
||||
case STATEMENT::OBRACE:
|
||||
std::cout << "{";
|
||||
break;
|
||||
|
||||
case STATEMENT::EBRACE:
|
||||
std::cout << "}";
|
||||
break;
|
||||
|
||||
case STATEMENT::DECL:
|
||||
std::cout << "decl " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::ASSIGN:
|
||||
std::cout << "assign " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::MALLOC:
|
||||
std::cout << "malloc " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::FREE:
|
||||
std::cout << "free " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::NEW:
|
||||
std::cout << "new " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::NEWARRAY:
|
||||
std::cout << "new[] " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::DELETE:
|
||||
std::cout << "delete " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::DELETEARRAY:
|
||||
std::cout << "delete[] " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::USE:
|
||||
std::cout << "use " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
|
||||
case STATEMENT::LOOP:
|
||||
std::cout << "loop";
|
||||
break;
|
||||
|
||||
case STATEMENT::ENDLOOP:
|
||||
std::cout << "endloop";
|
||||
break;
|
||||
|
||||
|
||||
case STATEMENT::SWITCH:
|
||||
std::cout << "switch";
|
||||
break;
|
||||
|
||||
case STATEMENT::ENDSWITCH:
|
||||
std::cout << "endswitch";
|
||||
break;
|
||||
|
||||
|
||||
case STATEMENT::IF:
|
||||
std::cout << "if";
|
||||
break;
|
||||
|
||||
case STATEMENT::ELSEIF:
|
||||
std::cout << "elseif";
|
||||
break;
|
||||
|
||||
case STATEMENT::ELSE:
|
||||
std::cout << "else";
|
||||
break;
|
||||
|
||||
case STATEMENT::ENDIF:
|
||||
std::cout << "endif";
|
||||
break;
|
||||
|
||||
case STATEMENT::RETURN:
|
||||
std::cout << "return " << VariableNames[s.VarIndex];
|
||||
break;
|
||||
|
||||
case STATEMENT::CONTINUE:
|
||||
std::cout << "continue";
|
||||
break;
|
||||
|
||||
case STATEMENT::BREAK:
|
||||
std::cout << "break";
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << "ERROR. Unknown code!!";
|
||||
break;
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#ifndef StatementsH
|
||||
#define StatementsH
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
extern std::vector<std::string> VariableNames;
|
||||
|
||||
// Forward declaration
|
||||
struct TOKEN;
|
||||
|
||||
struct STATEMENT
|
||||
{
|
||||
enum etype {OBRACE, EBRACE,
|
||||
DECL, ASSIGN, USE,
|
||||
MALLOC, FREE,
|
||||
NEW, DELETE,
|
||||
NEWARRAY, DELETEARRAY,
|
||||
LOOP, ENDLOOP,
|
||||
IF, ELSE, ELSEIF, ENDIF,
|
||||
SWITCH, ENDSWITCH,
|
||||
RETURN, CONTINUE, BREAK};
|
||||
etype Type;
|
||||
unsigned int VarIndex;
|
||||
TOKEN *Token;
|
||||
};
|
||||
|
||||
extern std::list<STATEMENT> Statements;
|
||||
|
||||
void CreateStatementList();
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
|
@ -4,7 +4,8 @@
|
|||
<MACROS>
|
||||
<VERSION value="BCB.06.00"/>
|
||||
<PROJECT value="cppcheck.exe"/>
|
||||
<OBJFILES value="main.obj tokenize.obj"/>
|
||||
<OBJFILES value="main.obj tokenize.obj CheckMemoryLeak.obj CommonCheck.obj Statements.obj
|
||||
CheckBufferOverrun.obj CheckClass.obj CheckHeaders.obj"/>
|
||||
<RESFILES value="cppcheck.res"/>
|
||||
<DEFFILE value=""/>
|
||||
<RESDEPEN value="$(RESFILES)"/>
|
||||
|
@ -54,6 +55,12 @@
|
|||
<FILE FILENAME="cppcheck.bpf" FORMNAME="" UNITNAME="cppcheck" CONTAINERID="BPF" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="main.cpp" FORMNAME="" UNITNAME="main" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="tokenize.cpp" FORMNAME="" UNITNAME="tokenize" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="CheckMemoryLeak.cpp" FORMNAME="" UNITNAME="CheckMemoryLeak.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="CommonCheck.cpp" FORMNAME="" UNITNAME="CommonCheck.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="Statements.cpp" FORMNAME="" UNITNAME="Statements.cpp" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="CheckBufferOverrun.cpp" FORMNAME="" UNITNAME="CheckBufferOverrun" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="CheckClass.cpp" FORMNAME="" UNITNAME="CheckClass" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
<FILE FILENAME="CheckHeaders.cpp" FORMNAME="" UNITNAME="CheckHeaders" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
|
||||
</FILELIST>
|
||||
<BUILDTOOLS>
|
||||
</BUILDTOOLS>
|
||||
|
|
99
tokenize.cpp
99
tokenize.cpp
|
@ -11,12 +11,7 @@
|
|||
std::vector<std::string> Files;
|
||||
struct TOKEN *tokens, *tokens_back;
|
||||
|
||||
// These functions are in "main.cpp"
|
||||
bool match(struct TOKEN *tok, const std::string pattern);
|
||||
TOKEN *gettok(struct TOKEN *tok, int index);
|
||||
const char *getstr(struct TOKEN *tok, int index);
|
||||
|
||||
|
||||
extern bool IsName(const char str[]);
|
||||
|
||||
struct DefineSymbol
|
||||
{
|
||||
|
@ -408,3 +403,95 @@ void Tokenize(const char FileName[])
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Helper functions for handling the tokens list
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
TOKEN *findtoken(TOKEN *tok1, const char *tokenstr[])
|
||||
{
|
||||
for (TOKEN *ret = tok1; ret; ret = ret->next)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
TOKEN *tok = ret;
|
||||
while (tokenstr[i])
|
||||
{
|
||||
if (!tok)
|
||||
return NULL;
|
||||
if (*(tokenstr[i]) && strcmp(tokenstr[i],tok->str))
|
||||
break;
|
||||
tok = tok->next;
|
||||
i++;
|
||||
}
|
||||
if (!tokenstr[i])
|
||||
return ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool match(TOKEN *tok, const std::string pattern)
|
||||
{
|
||||
if (!tok)
|
||||
return false;
|
||||
|
||||
const char *p = pattern.c_str();
|
||||
while (*p)
|
||||
{
|
||||
char str[50];
|
||||
char *s = str;
|
||||
while (*p==' ')
|
||||
p++;
|
||||
while (*p && *p!=' ')
|
||||
{
|
||||
*s = *p;
|
||||
s++;
|
||||
p++;
|
||||
}
|
||||
*s = 0;
|
||||
if (str[0] == 0)
|
||||
return true;
|
||||
|
||||
if (strcmp(str,"var")==0 || strcmp(str,"type")==0)
|
||||
{
|
||||
if (!IsName(tok->str))
|
||||
return false;
|
||||
}
|
||||
else if (strcmp(str,"num")==0)
|
||||
{
|
||||
if (!std::isdigit(tok->str[0]))
|
||||
return false;
|
||||
}
|
||||
else if (strcmp(str, tok->str) != 0)
|
||||
return false;
|
||||
|
||||
tok = tok->next;
|
||||
if (!tok)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
TOKEN *gettok(TOKEN *tok, int index)
|
||||
{
|
||||
while (tok && index>0)
|
||||
{
|
||||
tok = tok->next;
|
||||
index--;
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
const char *getstr(TOKEN *tok, int index)
|
||||
{
|
||||
tok = gettok(tok, index);
|
||||
return tok ? tok->str : "";
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,13 @@ extern struct TOKEN *tokens, *tokens_back;
|
|||
|
||||
void Tokenize(const char FileName[]);
|
||||
|
||||
|
||||
// Helper functions for handling the tokens list..
|
||||
TOKEN *findtoken(TOKEN *tok1, const char *tokenstr[]);
|
||||
bool match(TOKEN *tok, const std::string pattern);
|
||||
TOKEN *gettok(TOKEN *tok, int index);
|
||||
const char *getstr(TOKEN *tok, int index);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue