void ImplicitListChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::ListExp & le = static_cast<const ast::ListExp &>(e); if (le.getStart().isDoubleExp() && le.getStep().isDoubleExp() && le.getEnd().isDoubleExp()) { const double start = static_cast<const ast::DoubleExp &>(le.getStart()).getValue(); const double step = static_cast<const ast::DoubleExp &>(le.getStep()).getValue(); const double end = static_cast<const ast::DoubleExp &>(le.getEnd()).getValue(); if (ISNAN(start) || ISNAN(step) || ISNAN(end) || !finite(start) || !finite(step) || !finite(end)) { result.report(context, e.getLocation(), *this, _("Invalid list, it contains NaN or Inf.")); } else { if (start == end) { result.report(context, e.getLocation(), *this, _("List has the same start and end.")); } if (step == 0) { result.report(context, e.getLocation(), *this, _("List has a null step.")); } if ((start > end && step > 0) || (start < end && step < 0)) { result.report(context, e.getLocation(), *this, _("List is empty.")); } } } else if (le.getStart().isListExp() || le.getStep().isListExp() || le.getEnd().isListExp()) { result.report(context, e.getLocation(), *this, _("Bad use of ':' operator.")); } }
void DeprecatedChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::CallExp & ce = static_cast<const ast::CallExp &>(e); if (ce.getName().isSimpleVar()) { const std::wstring & name = static_cast<const ast::SimpleVar &>(ce.getName()).getSymbol().getName(); const auto i = deprecated.find(name); if (i != deprecated.end()) { if (i->second.empty()) { result.report(context, e.getLocation(), *this, _("Deprecated function: %s."), name); } else { result.report(context, e.getLocation(), *this, _("Deprecated function %s: use %s instead."), name, i->second); } } else { const auto i = partiallyDeprecated.find(name); if (i != partiallyDeprecated.end()) { i->second->preCheckNode(e, context, result); } } } }
bool OperAnalyzer::analyze(AnalysisVisitor & visitor, ast::Exp & e) { ast::OpExp & oe = static_cast<ast::OpExp &>(e); const ast::OpExp::Oper oper = oe.getOper(); if (oper == ast::OpExp::plus || oper == ast::OpExp::minus || oper == ast::OpExp::times) { if (ast::MemfillExp * mfe = analyzeMemfill(visitor, oe)) { mfe->setVerbose(e.isVerbose()); e.replace(mfe); return true; } } /*if (ast::ExtendedOpExp * eoe = analyzeMemfill(visitor, oe)) { eoe->setVerbose(e.isVerbose()); e.replace(eoe); return true; }*/ return false; }
void EmptyBlockChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { if (e.isSeqExp()) { const ast::exps_t & exps = static_cast<const ast::SeqExp &>(e).getExps(); bool empty = exps.empty(); if (!empty) { empty = true; for (const auto exp : exps) { if (!exp->isCommentExp()) { empty = false; break; } } } if (empty) { result.report(context, e.getLocation(), *this, _("Empty block.")); } } }
void IllegalCallsChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { if (e.getParent()->isCallExp() || e.getParent()->isSeqExp()) { const std::wstring & name = static_cast<const ast::SimpleVar &>(e).getSymbol().getName(); if (illegal.find(name) != illegal.end()) { result.report(context, e.getLocation(), *this, _("Illegal call: %s."), name); } } }
void FunctionArgsOrderChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::CallExp & ce = static_cast<const ast::CallExp &>(e); if (ce.getName().isSimpleVar()) { ast::exps_t args = ce.getArgs(); std::map<symbol::Symbol, unsigned int> assignments; unsigned int pos = 1; for (const auto arg : args) { if (arg->isAssignExp()) { const ast::AssignExp & ae = *static_cast<const ast::AssignExp *>(arg); if (ae.getLeftExp().isSimpleVar()) { assignments.emplace(static_cast<ast::SimpleVar &>(ae.getLeftExp()).getSymbol(), pos); } } else if (!assignments.empty()) { result.report(context, e.getLocation(), *this, _("Argument at position %d must be an assignment."), pos); } ++pos; } if (!assignments.empty()) { const std::wstring & name = static_cast<const ast::SimpleVar &>(ce.getName()).getSymbol().getName(); const ast::FunctionDec * fundec = context.getPublicFunction(name); if (!fundec) { fundec = context.getPrivateFunction(name); } if (fundec) { const ast::exps_t & funargs = fundec->getArgs().getVars(); pos = 1; for (const auto arg : funargs) { const symbol::Symbol & sym = static_cast<const ast::SimpleVar *>(arg)->getSymbol(); auto i = assignments.find(sym); if (i != assignments.end()) { if (pos != i->second) { result.report(context, e.getLocation(), *this, _("Argument %s declared at position %d and assigned at position %d."), sym.getName(), pos, i->second); } } ++pos; } } } } }
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 DeprecatedChecker::__Mfprintf::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::CallExp & ce = static_cast<const ast::CallExp &>(e); const ast::exps_t args = ce.getArgs(); if (args.size() != 0) { const ast::Exp & first = *args.front(); if (first.isDoubleExp() && static_cast<const ast::DoubleExp &>(first).getValue() == -1) { result.report(context, e.getLocation(), *this, _("mfprintf(-1, ...) is deprecated.")); } } }
void DeprecatedChecker::__Svd::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::CallExp & ce = static_cast<const ast::CallExp &>(e); const ast::exps_t args = ce.getArgs(); if (args.size() == 2) { const ast::Exp & second = *args.back(); if (second.isDoubleExp() && static_cast<const ast::DoubleExp &>(second).getValue() == 0) { result.report(context, e.getLocation(), *this, _("svd(..., 0) is deprecated.")); } } }
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::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 UnreachableCodeChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::exps_t & exps = e.getExps(); const ast::Exp * returnExp = nullptr; for (const auto exp : exps) { if (returnExp) { if (!exp->isCommentExp()) { result.report(context, returnExp->getLocation(), *this, _("The code after the return statement is unreachable.")); return; } } else if (exp->isReturnExp()) { returnExp = exp; } } }
bool ImplicitList::invoke(typed_list & in, optional_list & /*opt*/, int /*_iRetCount*/, typed_list & out, const ast::Exp & e) { if (in.size() == 0) { out.push_back(this); } else { InternalType * _out = extract(&in); if (!_out) { std::wostringstream os; os << _W("Invalid index.\n"); throw ast::InternalError(os.str(), 999, e.getLocation()); } out.push_back(_out); } return true; }
void FunctionTestReturnChecker::preCheckNode(const ast::Exp & e, SLintContext & context, SLintResult & result) { const ast::CallExp & ce = static_cast<const ast::CallExp &>(e); if (ce.getName().isSimpleVar()) { const std::wstring & name = static_cast<const ast::SimpleVar &>(ce.getName()).getSymbol().getName(); auto i = funs.find(name); if (i != funs.end()) { const std::vector<unsigned int> & positions = i->second; if (!positions.empty()) { const ast::AssignListExp * ale = nullptr; const ast::AssignExp * ae = context.getAssignExp(); if (ae) { if (ae->getLeftExp().isAssignListExp()) { ale = &static_cast<const ast::AssignListExp &>(ae->getLeftExp()); } // syms will contain the symbols associated to returned error code std::set<symbol::Symbol> syms; if (ale) { const ast::exps_t & exps = ale->getExps(); for (const auto pos : positions) { if (pos > exps.size()) { result.report(context, e.getLocation(), *this, _("Function %s requires an error checking just after call."), name); return; } else { if (exps[pos - 1] && exps[pos - 1]->isSimpleVar()) { syms.emplace(static_cast<ast::SimpleVar *>(exps[pos - 1])->getSymbol()); } } } } else if (ae->getLeftExp().isSimpleVar()) { if (positions.size() > 1 || positions.back() != 1) { result.report(context, e.getLocation(), *this, _("Function %s requires an error checking just after call."), name); return; } syms.emplace(static_cast<ast::SimpleVar &>(ae->getLeftExp()).getSymbol()); } if (const ast::Exp * next = context.getNextRelevantExp()) { if (next->isIfExp()) { const ast::IfExp * ie = static_cast<const ast::IfExp *>(next); // We look for syms in the test: found syms are removed from syms FindSymVisitor(syms, ie->getTest()); if (!syms.empty()) { result.report(context, e.getLocation(), *this, _("Function %s requires an error checking just after call."), name); return; } } } } else { result.report(context, e.getLocation(), *this, _("Function %s requires an error checking just after call."), name); return; } } } } }
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); }*/ } } }
bool AnalysisVisitor::getDimension(SymbolicDimension & dim, ast::Exp & arg, bool & safe, SymbolicDimension & out) { switch (arg.getType()) { case ast::Exp::COLONVAR : { out = dim; safe = true; arg.getDecorator().setDollarInfo(argIndices.top()); return true; } case ast::Exp::DOLLARVAR : // a($) { out = SymbolicDimension(getGVN(), 1.); safe = true; arg.getDecorator().setDollarInfo(argIndices.top()); return true; } case ast::Exp::DOUBLEEXP : // a(12) or a([1 2]) { ast::DoubleExp & de = static_cast<ast::DoubleExp &>(arg); if (types::InternalType * const pIT = de.getConstant()) { if (pIT->isDouble()) { types::Double * const pDbl = static_cast<types::Double *>(pIT); if (pDbl->isEmpty()) { out = SymbolicDimension(getGVN(), 0.); safe = true; return true; } const double * real = pDbl->getReal(); const int size = pDbl->getSize(); int64_t max; if (tools::asInteger(real[0], max)) { int64_t min = max; if (!pDbl->isComplex()) { for (int i = 0; i < size; ++i) { int64_t _real; if (tools::asInteger(real[i], _real)) { if (_real < min) { min = _real; } else if (_real > max) { max = _real; } } else { return false; } } out = SymbolicDimension(getGVN(), size); safe = (min >= 1) && getCM().check(ConstraintManager::GREATER, dim.getValue(), getGVN().getValue(max)); return true; } else { const double * imag = pDbl->getImg(); int i; for (i = 0; i < size; ++i) { if (imag[i]) { break; } int64_t _real; if (tools::asInteger(real[i], _real)) { if (_real < min) { min = _real; } else if (_real > max) { max = _real; } } } if (i == size) { out = SymbolicDimension(getGVN(), size); safe = (min >= 1) && getCM().check(ConstraintManager::GREATER, dim.getValue(), getGVN().getValue(max)); return true; } else { return false; } } } else { return false; } } else if (pIT->isImplicitList()) { types::ImplicitList * const pIL = static_cast<types::ImplicitList *>(pIT); double start, step, end; if (AnalysisVisitor::asDouble(pIL->getStart(), start) && AnalysisVisitor::asDouble(pIL->getStep(), step) && AnalysisVisitor::asDouble(pIL->getEnd(), end)) { double single; const int type = ForList64::checkList(start, end, step, single); switch (type) { case 0 : { out = SymbolicDimension(getGVN(), 0.); safe = true; return true; } case 1 : { out = SymbolicDimension(getGVN(), 1.); safe = false; return true; } case 2 : { const uint64_t N = ForList64::size(start, end, step); uint64_t max, min; if (step > 0) { min = start; max = (uint64_t)(start + (N - 1) * step); } else { max = start; min = (uint64_t)(start + (N - 1) * step); } out = SymbolicDimension(getGVN(), N); safe = (min >= 1) && getCM().check(ConstraintManager::GREATER, dim.getValue(), getGVN().getValue((int64_t)max)); return true; } } } } } else { out = SymbolicDimension(getGVN(), 1.); safe = (de.getValue() >= 1) && getCM().check(ConstraintManager::GREATER, dim.getValue(), getGVN().getValue(de.getValue())); return true; } return false; } case ast::Exp::BOOLEXP : // a(a > 1) => a([%f %t %t]) => a([2 3]) { ast::BoolExp & be = static_cast<ast::BoolExp &>(arg); if (types::InternalType * const pIT = be.getConstant()) { if (pIT->isBool()) { types::Bool * const pBool = static_cast<types::Bool *>(pIT); const int size = pBool->getSize(); const int * data = pBool->get(); int64_t max = -1; int64_t count = 0; for (int i = 0; i < size; ++i) { if (data[i]) { ++count; max = i; } } out = SymbolicDimension(getGVN(), count); safe = getCM().check(ConstraintManager::GREATER, dim.getValue(), getGVN().getValue(max)); return true; } } else { if (be.getValue()) { out = SymbolicDimension(getGVN(), int64_t(1)); } else { out = SymbolicDimension(getGVN(), int64_t(0)); } safe = true; return true; } return false; } case ast::Exp::LISTEXP : { ast::ListExp & le = static_cast<ast::ListExp &>(arg); SymbolicList sl; if (SymbolicList::get(*this, le, sl)) { if (sl.isSymbolic()) { sl.evalDollar(getGVN(), dim.getValue()); } TIType typ; if (sl.getType(getGVN(), typ)) { out = SymbolicDimension(getGVN(), typ.cols.getValue()); safe = false;//getCM().check(ConstraintManager::GREATER, dim.getValue(), getGVN().getValue(max)); return true; } } return false; } default : { arg.accept(*this); Result & _res = getResult(); SymbolicRange & range = _res.getRange(); if (range.isValid()) { //std::wcerr << *range.getStart()->poly << ":" << *range.getEnd()->poly << ",," << *dim.getValue()->poly << std::endl; safe = getCM().check(ConstraintManager::VALID_RANGE, range.getStart(), range.getEnd(), getGVN().getValue(int64_t(1)), dim.getValue()); out = _res.getType().rows * _res.getType().cols; return true; } if (GVN::Value * const v = _res.getConstant().getGVNValue()) { GVN::Value * w = v; if (GVN::Value * const dollar = getGVN().getExistingValue(symbol::Symbol(L"$"))) { if (GVN::Value * const x = SymbolicList::evalDollar(getGVN(), v, dollar, dim.getValue())) { w = x; } } bool b = getCM().check(ConstraintManager::GREATER, dim.getValue(), w); if (b) { safe = getCM().check(ConstraintManager::STRICT_POSITIVE, w); } else { safe = false; } out = SymbolicDimension(getGVN(), 1); return true; } // To use with find // e.g. a(find(a > 0)): find(a > 0) return a matrix where the max index is rc(a) so the extraction is safe if (_res.getType().ismatrix() && _res.getType().type != TIType::BOOLEAN) { out = _res.getType().rows * _res.getType().cols; SymbolicDimension & maxIndex = _res.getMaxIndex(); if (maxIndex.isValid()) { safe = getCM().check(ConstraintManager::GREATER, dim.getValue(), maxIndex.getValue()); } else { safe = false; } return true; } return false; } } }