commit 507c9f9f648f7587fbe94e97de21ddc663bf32a6 Author: Daniel Marjamäki Date: Mon May 7 17:31:35 2007 +0000 Added files from local repository. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..53022c474 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +checkcode.exe: main.cpp + gxx -Wall -pedantic -g -o checkcode.exe main.cpp diff --git a/main.cpp b/main.cpp new file mode 100644 index 000000000..9519f801e --- /dev/null +++ b/main.cpp @@ -0,0 +1,1104 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include + +//--------------------------------------------------------------------------- + +std::vector Files; + +struct TOKEN +{ + unsigned int FileIndex; + char *str; + unsigned int linenr; + struct TOKEN *next; +}; +struct TOKEN *tokens, *tokens_back; +//--------------------------------------------------------------------------- + +void Tokenize(const char FileName[]); + +// Class +void CheckConstructors(); +void CheckUnusedPrivateFunctions(); +void CheckMemset(); + +// Function +void CheckMovableVariableDeclaration(); + +// Casting +void WarningOldStylePointerCast(); + +// Headers.. +void WarningHeaderWithImplementation(); +void WarningIncludeHeader(); + +// Use standard functions instead +void WarningIsDigit(); + +// Redundant code +void WarningRedundantCode(); + +//--------------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + std::cout << "Syntax:\n checkcode filename\n"; + return 0; + } + + tokens = tokens_back = NULL; + Tokenize(argv[argc - 1]); + + std::ofstream f("tokens.txt"); + for (TOKEN *tok = tokens; tok; tok = tok->next) + f << tok->linenr << ":" << tok->str << '\n'; + f.close(); + + // Check that all class constructors are ok. + CheckConstructors(); + + // Check that all private functions are called. + CheckUnusedPrivateFunctions(); + + // Check that the memsets are valid. + // This function can do dangerous things if used wrong. + CheckMemset(); + + WarningHeaderWithImplementation(); + + // Warning upon c-style pointer casts + WarningOldStylePointerCast(); + + // Use standard functions instead + WarningIsDigit(); + + // Including header + //WarningIncludeHeader(); + + // + WarningRedundantCode(); + + return 0; +} +//--------------------------------------------------------------------------- + +void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno) +{ + if (str[0] == 0) + return; + + TOKEN *newtoken = new TOKEN; + memset(newtoken, 0, sizeof(TOKEN)); + newtoken->str = strdup(str); + newtoken->linenr = lineno; + newtoken->FileIndex = fileno; + if (tokens_back) + { + tokens_back->next = newtoken; + tokens_back = newtoken; + } + else + { + tokens = tokens_back = newtoken; + } +} +//--------------------------------------------------------------------------- + +void combine_2tokens(TOKEN *tok, const char str1[], const char str2[]) +{ + if (!(tok && tok->next)) + return; + if (strcmp(tok->str,str1) || strcmp(tok->next->str,str2)) + return; + + free(tok->str); + free(tok->next->str); + tok->str = static_cast(malloc(strlen(str1)+strlen(str2)+1)); + strcpy(tok->str, str1); + strcat(tok->str, str2); + + TOKEN *toknext = tok->next; + tok->next = toknext->next; + delete toknext; +} +//--------------------------------------------------------------------------- + +void Tokenize(const char FileName[]) +{ + std::ifstream fin(FileName); + if (!fin.is_open()) + return; + + unsigned int CurrentFile = Files.size(); + Files.push_back(FileName); + + unsigned int lineno = 1; + char CurrentToken[100]; + memset(CurrentToken, 0, sizeof(CurrentToken)); + char *pToken = CurrentToken; + for (char ch = (char)fin.get(); !fin.eof(); ch = (char)fin.get()) + { + if (ch == '#' && !CurrentToken[0]) + { + std::string line; + getline(fin,line); + line = "#" + line; + if (strncmp(line.c_str(),"#include",8)==0 && + line.find("\"") != std::string::npos) + { + // Extract the filename + line.erase(0, line.find("\"")+1); + line.erase(line.find("\"")); + } + + // Relative path.. + if (strchr(FileName,'\\')) + { + char path[1000]; + memset(path,0,sizeof(path)); + const char *p = strrchr(FileName, '\\'); + memcpy(path, FileName, p-FileName+1); + line = path + line; + } + + addtoken("#include", lineno, CurrentFile); + addtoken(line.c_str(), lineno, CurrentFile); + + Tokenize(line.c_str()); + lineno++; + continue; + } + + if (ch == '\n') + { + // Add current token.. + addtoken(CurrentToken, lineno++, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + pToken = CurrentToken; + continue; + } + + // Comments.. + if (ch == '/' && !fin.eof()) + { + // Add current token.. + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + pToken = CurrentToken; + + // Read next character.. + ch = (char)fin.get(); + + // If '//'.. + if (ch == '/') + { + while (!fin.eof() && (char)fin.get()!='\n'); + lineno++; + continue; + } + + // If '/*'.. + if (ch == '*') + { + char chPrev; + ch = chPrev = 'A'; + while (!fin.eof() && (chPrev!='*' || ch!='/')) + { + chPrev = ch; + ch = (char)fin.get(); + if (ch == '\n') + lineno++; + } + continue; + } + + // Not a comment.. add token.. + addtoken("/", lineno, CurrentFile); + } + + // char.. + if (ch == '\'') + { + // Add previous token + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + + // Read this .. + CurrentToken[0] = ch; + CurrentToken[1] = (char)fin.get(); + CurrentToken[2] = (char)fin.get(); + if (CurrentToken[1] == '\\') + CurrentToken[3] = (char)fin.get(); + + // Add token and start on next.. + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + pToken = CurrentToken; + + continue; + } + + // String.. + if (ch == '\"') + { + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + pToken = CurrentToken; + bool special = false; + char c = ch; + do + { + // Append token.. + *pToken = c; + pToken++; + + // Special sequence '\.' + if (special) + special = false; + else + special = (c == '\\'); + + // Get next character + c = (char)fin.get(); + } + while (special || c != '\"'); + *pToken = '\"'; + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + pToken = CurrentToken; + continue; + } + + if (strchr("+-*/%&|^?!=<>[](){};:,.",ch)) + { + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + CurrentToken[0] = ch; + addtoken(CurrentToken, lineno, CurrentFile); + memset(CurrentToken, 0, sizeof(CurrentToken)); + pToken = CurrentToken; + continue; + } + + + if (std::isspace(ch) || std::iscntrl(ch)) + { + addtoken(CurrentToken, lineno, CurrentFile); + pToken = CurrentToken; + memset(CurrentToken, 0, sizeof(CurrentToken)); + continue; + } + + *pToken = ch; + pToken++; + } + + // Combine tokens.. + for (TOKEN *tok = tokens; tok && tok->next; tok = tok->next) + { + combine_2tokens(tok, "<", "<"); + combine_2tokens(tok, ">", ">"); + + combine_2tokens(tok, "&", "&"); + combine_2tokens(tok, "|", "|"); + + combine_2tokens(tok, "+", "="); + combine_2tokens(tok, "-", "="); + combine_2tokens(tok, "*", "="); + combine_2tokens(tok, "/", "="); + combine_2tokens(tok, "&", "="); + combine_2tokens(tok, "|", "="); + + combine_2tokens(tok, "=", "="); + combine_2tokens(tok, "!", "="); + combine_2tokens(tok, "<", "="); + combine_2tokens(tok, ">", "="); + + combine_2tokens(tok, ":", ":"); + combine_2tokens(tok, "-", ">"); + + combine_2tokens(tok, "private", ":"); + combine_2tokens(tok, "protected", ":"); + combine_2tokens(tok, "public", ":"); + } +} +//--------------------------------------------------------------------------- + + + + + +void ReportErr(const std::string errmsg) +{ + std::cerr << errmsg << std::endl; +} +//--------------------------------------------------------------------------- + + + +bool IsName(const char str[]) +{ + return (str[0]=='_' || isalpha(str[0])); +} + +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; +} + +static 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 (!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 : ""; +} + + +std::string FileLine(TOKEN *tok) +{ + std::ostringstream ostr; + ostr << "[" << Files[tok->FileIndex] << ":" << tok->linenr << "]"; + return ostr.str(); +} +//--------------------------------------------------------------------------- + + +struct VAR +{ + bool is_class; + const char *name; + bool init; + bool is_pointer; + struct VAR *next; +}; +//--------------------------------------------------------------------------- + +struct VAR *ClassChecking_GetVarList(const char classname[]) +{ + // Locate class.. + const char *pattern[] = {"class","","{",NULL}; + 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; +} +//--------------------------------------------------------------------------- + +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; +} + + + + + +//--------------------------------------------------------------------------- +// 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 ); + } +} + + + +//--------------------------------------------------------------------------- +// Check: 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; + + // Get private functions.. + std::list FuncList; + FuncList.clear(); + bool priv = false; + unsigned int indent_level = 0; + for (TOKEN *tok = tok1; tok; tok = tok->next) + { + 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 (isalpha(tok->str[0]) && tok->next->str[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()) + { + std::ostringstream ostr; + ostr << "Class '" << classname << "', unused private function: '" << FuncList.front() << "'"; + ReportErr(ostr.str()); + FuncList.pop_front(); + } + } +} + +//--------------------------------------------------------------------------- +// Class: 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); + + if (!(type && type[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; + } + } + } +} + + + + + +//--------------------------------------------------------------------------- +// HEADERS - No implementation in a header +//--------------------------------------------------------------------------- + +void WarningHeaderWithImplementation() +{ + for (TOKEN *tok = tokens; tok; tok = tok->next) + { + // Only interested in included file + if (tok->FileIndex == 0) + continue; + + if (match(tok, ") {")) + { + std::ostringstream ostr; + ostr << FileLine(tok) << ": Found implementation in header"; + ReportErr(ostr.str()); + } + } +} + + + + + + +//--------------------------------------------------------------------------- +// Warning on C-Style casts.. p = (kalle *)foo; +//--------------------------------------------------------------------------- + +void WarningOldStylePointerCast() +{ + for (TOKEN *tok = tokens; tok; tok = tok->next) + { + // Old style pointer casting.. + if (!match(tok, "( type * ) var")) + continue; + + // Is "type" a class? + const char *pattern[] = {"class","",NULL}; + pattern[1] = getstr(tok, 1); + if (!findtoken(tokens, pattern)) + continue; + + std::ostringstream ostr; + ostr << FileLine(tok) << ": C-style pointer casting"; + ReportErr(ostr.str()); + } +} + + + + +//--------------------------------------------------------------------------- +// Use standard function "isdigit" instead +//--------------------------------------------------------------------------- + +void WarningIsDigit() +{ + for (TOKEN *tok = tokens; tok; tok = tok->next) + { + bool err = false; + err |= match(tok, "var >= '0' && var <= '9'"); + err |= match(tok, "* var >= '0' && * var <= '9'"); + err |= match(tok, "( var >= '0' ) && ( var <= '9' )"); + err |= match(tok, "( * var >= '0' ) && ( * var <= '9' )"); + if (err) + { + std::ostringstream ostr; + ostr << FileLine(tok) << ": The condition can be simplified; use 'isdigit'"; + ReportErr(ostr.str()); + } + } +} +//--------------------------------------------------------------------------- + +void WarningIncludeHeader() +{ + // Including.. + for (TOKEN *includetok = tokens; includetok; includetok = includetok->next) + { + if (strcmp(includetok->str, "#include") != 0) + continue; + + // Get fileindex of included file.. + unsigned int hfile = 0; + const char *includefile = includetok->next->str; + while (hfile < Files.size()) + { + if (stricmp(Files[hfile].c_str(), includefile) == 0) + break; + hfile++; + } + if (hfile == Files.size()) + continue; + + // This header is needed if: + // * It contains some needed class declaration + // * It contains some needed function declaration + // * It contains some needed constant value + // * It contains some needed variable + // * It contains some needed enum + bool Needed = false; + bool NeedDeclaration = false; + int indentlevel = 0; + for (TOKEN *tok1 = tokens; tok1; tok1 = tok1->next) + { + if (tok1->FileIndex != hfile) + continue; + + if (!tok1->next) + continue; + + if (!tok1->next->next) + continue; + + // I'm only interested in stuff that is declared at indentlevel 0 + if (tok1->str[0] == '{') + indentlevel++; + if (tok1->str[0] == '}') + indentlevel--; + if (indentlevel != 0) + continue; + + // Class or namespace declaration.. + if (match(tok1,"class var {") || + match(tok1,"class var :") || + match(tok1,"namespace var {")) + { + std::string classname = getstr(tok1, 1); + + // Try to find class usage in "parent" file.. + for (TOKEN *tok2 = tokens; tok2; tok2 = tok2->next) + { + if (tok2->FileIndex != includetok->FileIndex) + continue; + + // Inheritage.. + Needed |= match(tok2, "class var : " + classname); + Needed |= match(tok2, "class var : type " + classname); + + // Allocating.. + Needed |= match(tok2, "new " + classname); + + // Using class.. + Needed |= match(tok2, classname + " ::"); + Needed |= match(tok2, classname + " var"); + NeedDeclaration |= match(tok2, classname + " *"); + } + + if (Needed | NeedDeclaration) + break; + } + + // Variable.. + std::string varname = ""; + if (match(tok1, "type var ;") || match(tok1, "type var [")) + varname = getstr(tok1, 1); + if (match(tok1, "type * var ;") || match(tok1, "type * var [")) + varname = getstr(tok1, 2); + if (!varname.empty()) + { + for (TOKEN *tok2 = tokens; tok2; tok2 = tok2->next) + { + if (tok2->FileIndex != includetok->FileIndex) + continue; + + NeedDeclaration |= (tok2->str == varname); + Needed |= match(tok2, varname + " ."); + Needed |= match(tok2, varname + " ->"); + Needed |= match(tok2, varname + " ="); + } + + if (Needed | NeedDeclaration) + break; + } + + // enum + if (match(tok1,"enum var {")) + { + std::string enumname = getstr(tok1, 1); + + // Try to find enum usage in "parent" file.. + for (TOKEN *tok2 = tokens; tok2; tok2 = tok2->next) + { + if (tok2->FileIndex != includetok->FileIndex) + continue; + + Needed |= (enumname == tok2->str); + } + + if (Needed) + break; + } + + } + + + // Not a header file? + if (includetok->FileIndex == 0) + Needed |= NeedDeclaration; + + // Not needed! + if (!Needed) + { + std::ostringstream ostr; + ostr << FileLine(includetok) << ": The included header '" << includefile << "' is not needed"; + if (NeedDeclaration) + ostr << " (but a declaration in it is needed)"; + ReportErr(ostr.str()); + } + } +} +//--------------------------------------------------------------------------- + +void WarningRedundantCode() +{ + for (TOKEN *tok = tokens; tok; tok = tok->next) + { + if (strcmp(tok->str,"if")) + continue; + + const char *varname1 = NULL; + TOKEN *tok2 = NULL; + + if (match(tok,"if ( var )")) + { + varname1 = getstr(tok, 2); + tok2 = gettok(tok, 4); + } + else if (match(tok,"if ( var != NULL )")) + { + varname1 = getstr(tok, 2); + tok2 = gettok(tok, 6); + } + + if (varname1==NULL || tok2==NULL) + continue; + + bool err = false; + if (match(tok2,"delete var ;")) + err = (strcmp(getstr(tok2,1),varname1)==0); + else if (match(tok2,"{ delete var ; }")) + err = (strcmp(getstr(tok2,2),varname1)==0); + else if (match(tok2,"free ( var )")) + err = (strcmp(getstr(tok2,2),varname1)==0); + else if (match(tok2,"{ free ( var ) ; }")) + err = (strcmp(getstr(tok2,3),varname1)==0); + + if (err) + { + std::ostringstream ostr; + ostr << FileLine(tok) << ": Redundant condition. It is allowed to deallocate a NULL pointer, so it is safe to remove the 'if(..)'."; + ReportErr(ostr.str()); + } + } +} + diff --git a/runall.bat b/runall.bat new file mode 100644 index 000000000..16f4f9f40 --- /dev/null +++ b/runall.bat @@ -0,0 +1,15 @@ + +@ECHO OFF + +REM Test the 'checkcode' program + +date /t > report.txt +time /t >> report.txt + +FOR /D %%s IN (test*) DO ( + checkcode %%s\%%s.cpp 2> %%s\out.msg + hydfc %%s\err.msg %%s\out.msg >> report.txt +) + +type report.txt + diff --git a/testclass1/err.msg b/testclass1/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testclass1/testclass1.cpp b/testclass1/testclass1.cpp new file mode 100644 index 000000000..22af1d183 --- /dev/null +++ b/testclass1/testclass1.cpp @@ -0,0 +1,8 @@ +// No constructor +// uninitialized member variable: 'i' + +class clKalle +{ +public: + int i; +}; diff --git a/testclass10/err.msg b/testclass10/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testclass10/testclass10.cpp b/testclass10/testclass10.cpp new file mode 100644 index 000000000..c8d844562 --- /dev/null +++ b/testclass10/testclass10.cpp @@ -0,0 +1,15 @@ + + +class clKalle +{ +private: + int i; +public: + clKalle(); +}; + +clKalle::clKalle() : i(0) +{ + +} + diff --git a/testclass2/err.msg b/testclass2/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testclass2/testclass2.cpp b/testclass2/testclass2.cpp new file mode 100644 index 000000000..f549b4f49 --- /dev/null +++ b/testclass2/testclass2.cpp @@ -0,0 +1,11 @@ +// uninitialized member variable: 'i' + +class clKalle +{ +public: + clKalle() + { + + } + int i; +}; diff --git a/testclass4/err.msg b/testclass4/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testclass4/testclass4.cpp b/testclass4/testclass4.cpp new file mode 100644 index 000000000..fe14a3c6d --- /dev/null +++ b/testclass4/testclass4.cpp @@ -0,0 +1,10 @@ + +#include "testclass4.h" + + +clKalle::clKalle() +{ + i = ds23; +} + + diff --git a/testclass4/testclass4.h b/testclass4/testclass4.h new file mode 100644 index 000000000..15570a906 --- /dev/null +++ b/testclass4/testclass4.h @@ -0,0 +1,8 @@ + +class clKalle +{ +public: + int i; + clKalle(); +}; + diff --git a/testclass5/err.msg b/testclass5/err.msg new file mode 100644 index 000000000..5e2ce22c4 --- /dev/null +++ b/testclass5/err.msg @@ -0,0 +1 @@ +Class 'clKalle', unused private function: 'f' diff --git a/testclass5/testclass5.cpp b/testclass5/testclass5.cpp new file mode 100644 index 000000000..b028bdcae --- /dev/null +++ b/testclass5/testclass5.cpp @@ -0,0 +1,21 @@ + +// Unused private function.. + +class clKalle +{ +private: + void f(); + void g(); +public: + clKalle(); +}; + +clKalle::clKalle() +{ + g(); +} + +void clKalle::f() +{ + +} \ No newline at end of file diff --git a/testclass6/err.msg b/testclass6/err.msg new file mode 100644 index 000000000..fca8f34ef --- /dev/null +++ b/testclass6/err.msg @@ -0,0 +1,2 @@ +[testclass6\testclass6.h:3]: Found implementation in header +[testclass6\testclass6.cpp:2]: The included header 'testclass6\testclass6.h' is not needed diff --git a/testclass6/testclass6.cpp b/testclass6/testclass6.cpp new file mode 100644 index 000000000..df7b09d58 --- /dev/null +++ b/testclass6/testclass6.cpp @@ -0,0 +1,8 @@ + +#include "testclass6.h" + +void clBertil::g() +{ + +} + diff --git a/testclass6/testclass6.h b/testclass6/testclass6.h new file mode 100644 index 000000000..df6cdf8fb --- /dev/null +++ b/testclass6/testclass6.h @@ -0,0 +1,7 @@ + + +class clKalle() +{ +private: + void f(); +}; \ No newline at end of file diff --git a/testclass7/err.msg b/testclass7/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testclass7/testclass7.cpp b/testclass7/testclass7.cpp new file mode 100644 index 000000000..80b18c1d3 --- /dev/null +++ b/testclass7/testclass7.cpp @@ -0,0 +1,21 @@ + +// Initializing class variables through a 'Clear' function + +class clKalle +{ +private: + int i; +public: + clKalle(); + void Clear(); +}; + +clKalle::clKalle() +{ + Clear(); +} + +void clKalle::Clear() +{ + i = 123; +} \ No newline at end of file diff --git a/testclass8/err.msg b/testclass8/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testclass8/testclass8.cpp b/testclass8/testclass8.cpp new file mode 100644 index 000000000..fbac8c60c --- /dev/null +++ b/testclass8/testclass8.cpp @@ -0,0 +1,14 @@ + + +class clKalle +{ +private: + std::vector ints; +public: + clKalle(); +}; + +clKalle::clKalle() +{ + ints.clear(); +} \ No newline at end of file diff --git a/testclass9/err.msg b/testclass9/err.msg new file mode 100644 index 000000000..cfa12d4d3 --- /dev/null +++ b/testclass9/err.msg @@ -0,0 +1,2 @@ +Uninitialized member variable 'clKalle::rec3' +Uninitialized member variable 'clKalle::rec1' diff --git a/testclass9/testclass9.cpp b/testclass9/testclass9.cpp new file mode 100644 index 000000000..4b5d5755c --- /dev/null +++ b/testclass9/testclass9.cpp @@ -0,0 +1,33 @@ + +// A struct must be initialized whenever it's used. +// But a class is supposed to have a constructor that +// performs the initialization. + +struct rec +{ + int a; + int b; +}; + +class clRec +{ +public: + int a; + int b; + rec2(); +}; + +class clKalle +{ +private: + rec rec1; + clRec rec2; + clRec *rec3; +public: + clKalle(); +}; + +clKalle::clKalle() +{ + +} \ No newline at end of file diff --git a/testdelete1/err.msg b/testdelete1/err.msg new file mode 100644 index 000000000..801e0023b --- /dev/null +++ b/testdelete1/err.msg @@ -0,0 +1 @@ +[testdelete1\testdelete1.cpp:6]: Redundant condition. It is allowed to deallocate a NULL pointer, so it is safe to remove the 'if(..)'. diff --git a/testdelete1/testdelete1.cpp b/testdelete1/testdelete1.cpp new file mode 100644 index 000000000..c04434f26 --- /dev/null +++ b/testdelete1/testdelete1.cpp @@ -0,0 +1,8 @@ + + + +void f() +{ + if (p) + delete p; +} \ No newline at end of file diff --git a/testfunc2/err.msg b/testfunc2/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testfunc2/testfunc2.cpp b/testfunc2/testfunc2.cpp new file mode 100644 index 000000000..69d0e23ab --- /dev/null +++ b/testfunc2/testfunc2.cpp @@ -0,0 +1,8 @@ + +void foo(int i) +{ + if (ab) + { + i = 4; + } +} diff --git a/testfunc3/err.msg b/testfunc3/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testfunc3/testfunc3.cpp b/testfunc3/testfunc3.cpp new file mode 100644 index 000000000..337ea3c27 --- /dev/null +++ b/testfunc3/testfunc3.cpp @@ -0,0 +1,28 @@ + + + + + + + +class ITEM +{ +public: + void CountIO( int &noIn, int &noOut ) const; +}; + + +void ITEM::CountIO( int &noIn, int &noOut ) const +{ + noIn = 0; + noOut = 0; +} + + + + + + + + + diff --git a/testh1/err.msg b/testh1/err.msg new file mode 100644 index 000000000..f92efd381 --- /dev/null +++ b/testh1/err.msg @@ -0,0 +1,3 @@ +[testh1\testh1.h:5]: Found implementation in header +[testh1\testh1.h:11]: Found implementation in header +[testh1\testh1.cpp:2]: The included header 'testh1\testh1.h' is not needed diff --git a/testh1/testh1.cpp b/testh1/testh1.cpp new file mode 100644 index 000000000..8f3421111 --- /dev/null +++ b/testh1/testh1.cpp @@ -0,0 +1,2 @@ + +#include "testh1.h" diff --git a/testh1/testh1.h b/testh1/testh1.h new file mode 100644 index 000000000..a75206ca6 --- /dev/null +++ b/testh1/testh1.h @@ -0,0 +1,14 @@ + + +// This is a header so there should be no implementation here.. + +void f() +{ } + +class clKalle +{ +public: + clKalle() + { } +}; + diff --git a/testh2/emptyh.h b/testh2/emptyh.h new file mode 100644 index 000000000..e69de29bb diff --git a/testh2/err.msg b/testh2/err.msg new file mode 100644 index 000000000..3891dc819 --- /dev/null +++ b/testh2/err.msg @@ -0,0 +1,2 @@ +[testh2\testh2.cpp:2]: The included header 'testh2\testh2.h' is not needed +[testh2\testh2.h:2]: The included header 'testh2\emptyh.h' is not needed diff --git a/testh2/testh2.cpp b/testh2/testh2.cpp new file mode 100644 index 000000000..e17d8a0eb --- /dev/null +++ b/testh2/testh2.cpp @@ -0,0 +1,4 @@ + +#include "testh2.h" + + diff --git a/testh2/testh2.h b/testh2/testh2.h new file mode 100644 index 000000000..da09b6086 --- /dev/null +++ b/testh2/testh2.h @@ -0,0 +1,7 @@ + +#include "emptyh.h" + +class clKalle +{ + +}; diff --git a/testh5/err.msg b/testh5/err.msg new file mode 100644 index 000000000..e69de29bb diff --git a/testh5/testh5.cpp b/testh5/testh5.cpp new file mode 100644 index 000000000..c7bc8e4da --- /dev/null +++ b/testh5/testh5.cpp @@ -0,0 +1,8 @@ + +#include "testh5.h" + +void f(E1 e1) +{ + +} + diff --git a/testh5/testh5.h b/testh5/testh5.h new file mode 100644 index 000000000..43338e3f2 --- /dev/null +++ b/testh5/testh5.h @@ -0,0 +1,4 @@ + + +enum E1 {a,b,c}; + diff --git a/testmemset1/err.msg b/testmemset1/err.msg new file mode 100644 index 000000000..3abf9a1dd --- /dev/null +++ b/testmemset1/err.msg @@ -0,0 +1,2 @@ +[testmemset1\testmemset1.cpp:28]: Using 'memset' on class. +[testmemset1\testmemset1.cpp:34]: Using 'memset' on struct that contains a 'std::string' diff --git a/testmemset1/testmemset1.cpp b/testmemset1/testmemset1.cpp new file mode 100644 index 000000000..cb6a246a6 --- /dev/null +++ b/testmemset1/testmemset1.cpp @@ -0,0 +1,36 @@ + +class clKalle +{ +private: + int i; +public: + clKalle(); +}; + +struct S1 +{ + char *str; +} + +struct S2 +{ + std::string str; +} + +clKalle::clKalle() +{ + i = 0; +} + +void f() +{ + clKalle *kalle = new clKalle; + memset(kalle, 0, sizeof(clKalle)); + + S1 s1; + memset(&s1, 0, sizeof(S1)); + + S2 s2; + memset(&s2, 0, sizeof(S2)); +} + diff --git a/teststdfunc1/err.msg b/teststdfunc1/err.msg new file mode 100644 index 000000000..1befb6e19 --- /dev/null +++ b/teststdfunc1/err.msg @@ -0,0 +1,2 @@ +[teststdfunc1\teststdfunc1.cpp:4]: The condition can be simplified; use 'isdigit' +[teststdfunc1\teststdfunc1.cpp:9]: The condition can be simplified; use 'isdigit' diff --git a/teststdfunc1/teststdfunc1.cpp b/teststdfunc1/teststdfunc1.cpp new file mode 100644 index 000000000..1338e2298 --- /dev/null +++ b/teststdfunc1/teststdfunc1.cpp @@ -0,0 +1,15 @@ + +void f(char ch) +{ + if (ch>='0' && ch<='9') + { + + } + + if (*p>='0' && *p<='9') + { + + } +} + +