void UselessRetChecker::postCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { if (e.isFunctionDec()) { const std::map<symbol::Symbol, Location> & map = useless.top(); for (const auto & p : map) { result.report(context, p.second, *this, _("Function returned value might be unused: %s."), p.first); } useless.pop(); } }
void VariablesChecker::postCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { if (e.isFunctionDec()) { const ast::FunctionDec & fd = static_cast<const ast::FunctionDec &>(e); auto & map = assigned.top(); const auto & out = context.getFunOut(); for (const auto & n : out) { map.erase(n); } map.erase(fd.getSymbol().getName()); for (const auto & p : map) { result.report(context, p.second.first, *this, _("Declared variable and might be unused: %s."), p.first); } assigned.pop(); used.pop(); } }
void UselessRetChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { if (e.isFunctionDec()) { const ast::exps_t & rets = static_cast<const ast::FunctionDec &>(e).getReturns().getVars(); useless.push(std::map<symbol::Symbol, Location>()); std::map<symbol::Symbol, Location> & map = useless.top(); for (const auto ret : rets) { const ast::SimpleVar & var = *static_cast<const ast::SimpleVar *>(ret); map.emplace(var.getSymbol(), var.getLocation()); } } else if (e.isSimpleVar()) { const ast::SimpleVar & var = static_cast<const ast::SimpleVar &>(e); if (context.isFunOut(var.getSymbol()) && context.isAssignedVar(var)) { useless.top().erase(var.getSymbol()); } } }
void VariablesChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { if (e.isFunctionDec()) { const ast::FunctionDec & fd = static_cast<const ast::FunctionDec &>(e); if (!assigned.empty()) { // we declare the function in the current scope std::pair<Location, ast::AssignListExp *> p = { e.getLocation(), nullptr }; assigned.top().emplace(fd.getSymbol().getName(), p); } assigned.emplace(std::unordered_map<std::wstring, std::pair<Location, ast::AssignListExp *>>()); used.emplace(std::unordered_map<std::wstring, const ast::Exp *>()); // a function cans refer to itself std::pair<Location, ast::AssignListExp *> p = { e.getLocation(), nullptr }; assigned.top().emplace(fd.getSymbol().getName(), p); } else { if (!used.empty()) { if (e.isSimpleVar()) { const ast::SimpleVar & var = static_cast<const ast::SimpleVar &>(e); if (context.isAssignedVar(var)) { // if we are not in the context on a nested assignment in a function call (foo(a,b=2)) if (!var.getParent()->getParent() || !var.getParent()->getParent()->isCallExp()) { const std::wstring & name = var.getSymbol().getName(); auto i = used.top().find(name); if (!context.topLoop() || (i == used.top().end()) || !isParentOf(context.topLoop(), i->second)) { // the variable has never been used or the last time it wasn't in a loop if (i != used.top().end()) { used.top().erase(i); } if (var.getParent() == context.getLHSExp() && var.getParent()->isAssignListExp()) { // we have something like [lhs, rhs] = argn(0) // if rhs is used and lhs is not used then this not an "error": // we are obliged to define lhs just to get rhs. // So when rhs is used lhs is "used" too. std::pair<Location, ast::AssignListExp *> p = { var.getLocation(), static_cast<ast::AssignListExp *>(var.getParent()) }; assigned.top().emplace(name, p); } else { std::pair<Location, ast::AssignListExp *> p = { var.getLocation(), nullptr }; assigned.top().emplace(name, p); } } else /*if (context.topLoop() && i != used.top.end() && isParentOf(context.topLoop(), i->second))*/ { // Just to remember... // Here the variable has already been used in the current loop and we reassign it // something like: // while (...) // ... use i // ... // i = ... // end // In general the loop will looping so the assignment is implicitly destroyed by the use in the next iteration // so we don't add this assignment ! } } } else if (!e.getParent()->isFieldExp() || static_cast<const ast::FieldExp *>(e.getParent())->getTail() != &e) { const symbol::Symbol & sym = var.getSymbol(); const std::wstring & name = sym.getName(); if (used.top().find(name) == used.top().end()) { used.top().emplace(name, context.topLoop()); auto i = assigned.top().find(name); if (i == assigned.top().end()) { if (!context.isFunIn(name) && !SLintChecker::isScilabConstant(name)) { types::InternalType * pIT = symbol::Context::getInstance()->get(sym); if (pIT) { if (!pIT->isFunction() && !pIT->isMacroFile() && !pIT->isMacro()) { result.report(context, e.getLocation(), *this, _("Use of non-initialized variable \'%s\' may have any side-effects."), name); } } else if (!context.isPrivateFunction(sym)) { /* The symbol doesn't correspond to a private function: function tata() titi() end function titi() ... end titi is private but usable in tata. */ std::wstring fname; if (context.isExternPrivateFunction(sym, fname)) { result.report(context, e.getLocation(), *this, _("Use of a private macro \'%s\' defined in an other file %s."), name, fname); } else if (!context.getPublicFunction(fname)) { // The macro has not been declared somewhere in the project result.report(context, e.getLocation(), *this, _("Use of non-initialized variable \'%s\' may have any side-effects."), name); } } } } else { if (ast::AssignListExp * ale = i->second.second) { // the variable is in an AssignListExp // so we must "use" the variables which preceed it too for (auto e : ale->getExps()) { if (e->isSimpleVar()) { const std::wstring & prevName = static_cast<ast::SimpleVar *>(e)->getSymbol().getName(); assigned.top().erase(prevName); if (prevName == name) { break; } else { used.top().emplace(prevName, context.topLoop()); } } } } else { assigned.top().erase(i); } } } } } // for i=1:10... end even if i is not used, i is useful to make the loop /*else if (e.isVarDec()) { const ast::VarDec & vd = static_cast<const ast::VarDec &>(e); const std::wstring & name = vd.getSymbol().getName(); std::pair<Location, ast::AssignListExp *> p = { vd.getLocation(), nullptr }; assigned.top().emplace(name, p); used.top().erase(name); }*/ } } }