|
19 | 19 | #include "checkstl.h" |
20 | 20 |
|
21 | 21 | #include "astutils.h" |
| 22 | +#include "calculate.h" |
22 | 23 | #include "errortypes.h" |
23 | 24 | #include "library.h" |
24 | 25 | #include "mathlib.h" |
25 | | -#include "refthunk.h" |
26 | 26 | #include "pathanalysis.h" |
| 27 | +#include "refthunk.h" |
27 | 28 | #include "settings.h" |
28 | 29 | #include "standards.h" |
29 | 30 | #include "symboldatabase.h" |
@@ -1734,34 +1735,77 @@ void CheckStlImpl::checkFindInsert() |
1734 | 1735 | if (!valueTok2) |
1735 | 1736 | continue; |
1736 | 1737 | if (isSameExpression(true, valueTok, valueTok2, mSettings, true, true)) { |
1737 | | - checkFindInsertError(valueTok); |
| 1738 | + // Both branches set the same value; the replacement depends on both branches, so no suggestion |
| 1739 | + checkFindInsertError(valueTok, nullptr); |
1738 | 1740 | } |
1739 | 1741 | } else { |
1740 | | - checkFindInsertError(valueTok); |
| 1742 | + checkFindInsertError(valueTok, condTok); |
1741 | 1743 | } |
1742 | 1744 | } |
1743 | 1745 | } |
1744 | 1746 | } |
1745 | 1747 |
|
1746 | | -void CheckStlImpl::checkFindInsertError(const Token *tok) |
1747 | | -{ |
1748 | | - std::string replaceExpr; |
1749 | | - if (tok && Token::simpleMatch(tok->astParent(), "=") && tok == tok->astParent()->astOperand2() && Token::simpleMatch(tok->astParent()->astOperand1(), "[")) { |
1750 | | - if (mSettings.standards.cpp < Standards::CPP11) |
1751 | | - // We will recommend using emplace/try_emplace instead |
1752 | | - return; |
1753 | | - const std::string f = (mSettings.standards.cpp < Standards::CPP17) ? "emplace" : "try_emplace"; |
1754 | | - replaceExpr = " Instead of '" + tok->astParent()->expressionString() + "' consider using '" + |
1755 | | - tok->astParent()->astOperand1()->astOperand1()->expressionString() + |
1756 | | - "." + f + "(" + |
1757 | | - tok->astParent()->astOperand1()->astOperand2()->expressionString() + |
1758 | | - ", " + |
1759 | | - tok->expressionString() + |
1760 | | - ");'."; |
| 1748 | +/** When is the condition of the 'if' statement checked by checkFindInsert() true? */ |
| 1749 | +enum class FindInsertCond : std::uint8_t { UNKNOWN, FOUND, NOT_FOUND }; |
| 1750 | + |
| 1751 | +static FindInsertCond getFindInsertCond(const Token* condTok) |
| 1752 | +{ |
| 1753 | + const Token* rhs = condTok->astOperand2(); |
| 1754 | + if (!rhs) |
| 1755 | + return FindInsertCond::UNKNOWN; |
| 1756 | + if (Token::simpleMatch(condTok->astOperand1()->astOperand1(), ". find (")) { |
| 1757 | + // c.find(k) == c.end() / c.find(k) != c.end() |
| 1758 | + if (!Token::simpleMatch(rhs, "(") || !Token::Match(rhs->astOperand1(), ". end|cend (")) |
| 1759 | + return FindInsertCond::UNKNOWN; |
| 1760 | + if (condTok->str() == "==") |
| 1761 | + return FindInsertCond::NOT_FOUND; |
| 1762 | + if (condTok->str() == "!=") |
| 1763 | + return FindInsertCond::FOUND; |
| 1764 | + return FindInsertCond::UNKNOWN; |
| 1765 | + } |
| 1766 | + // c.count(k) == 0, c.count(k) > 0, ...: count() yields 0 or 1 |
| 1767 | + if (!rhs->hasKnownIntValue()) |
| 1768 | + return FindInsertCond::UNKNOWN; |
| 1769 | + const MathLib::bigint n = rhs->getKnownIntValue(); |
| 1770 | + const bool found = calculate<MathLib::bigint, MathLib::bigint>(condTok->str(), 1, n) != 0; |
| 1771 | + const bool notFound = calculate<MathLib::bigint, MathLib::bigint>(condTok->str(), 0, n) != 0; |
| 1772 | + if (found == notFound) |
| 1773 | + return FindInsertCond::UNKNOWN; |
| 1774 | + return found ? FindInsertCond::FOUND : FindInsertCond::NOT_FOUND; |
| 1775 | +} |
| 1776 | + |
| 1777 | +void CheckStlImpl::checkFindInsertError(const Token* tok, const Token* condTok) |
| 1778 | +{ |
| 1779 | + std::string suggestion; |
| 1780 | + std::string method = "insertion"; |
| 1781 | + if (tok && condTok && Token::simpleMatch(tok->astParent(), "=") && tok == tok->astParent()->astOperand2() && |
| 1782 | + Token::simpleMatch(tok->astParent()->astOperand1(), "[")) { |
| 1783 | + const std::string containerExpr = tok->astParent()->astOperand1()->astOperand1()->expressionString(); |
| 1784 | + const std::string keyExpr = tok->astParent()->astOperand1()->astOperand2()->expressionString(); |
| 1785 | + switch (getFindInsertCond(condTok)) { |
| 1786 | + case FindInsertCond::NOT_FOUND: { |
| 1787 | + if (mSettings.standards.cpp < Standards::CPP11) |
| 1788 | + // We will recommend using emplace/try_emplace instead |
| 1789 | + return; |
| 1790 | + const std::string f = (mSettings.standards.cpp < Standards::CPP17) ? "emplace" : "try_emplace"; |
| 1791 | + suggestion = " Instead of '" + tok->astParent()->expressionString() + "' consider using '" + containerExpr + |
| 1792 | + "." + f + "(" + keyExpr + ", " + tok->expressionString() + ");'."; |
| 1793 | + break; |
| 1794 | + } |
| 1795 | + case FindInsertCond::FOUND: |
| 1796 | + // The key is present, so the element is modified rather than inserted; try_emplace() would not do that |
| 1797 | + method = "modification"; |
| 1798 | + suggestion = " Instead of '" + tok->astParent()->expressionString() + |
| 1799 | + "' consider assigning the value through the iterator returned by '" + containerExpr + |
| 1800 | + ".find(" + keyExpr + ")'."; |
| 1801 | + break; |
| 1802 | + case FindInsertCond::UNKNOWN: |
| 1803 | + break; |
| 1804 | + } |
1761 | 1805 | } |
| 1806 | + std::string msg = "Searching before " + method + " is not necessary." + suggestion; |
1762 | 1807 |
|
1763 | | - reportError( |
1764 | | - tok, Severity::performance, "stlFindInsert", "Searching before insertion is not necessary." + replaceExpr, CWE398, Certainty::normal); |
| 1808 | + reportError(tok, Severity::performance, "stlFindInsert", msg, CWE398, Certainty::normal); |
1765 | 1809 | } |
1766 | 1810 |
|
1767 | 1811 | /** |
@@ -3899,7 +3943,7 @@ void CheckStl::getErrorMessages(ErrorLogger& errorLogger, const Settings& settin |
3899 | 3943 | c.stlBoundariesError(nullptr); |
3900 | 3944 | c.if_findError(nullptr, false); |
3901 | 3945 | c.if_findError(nullptr, true); |
3902 | | - c.checkFindInsertError(nullptr); |
| 3946 | + c.checkFindInsertError(nullptr, nullptr); |
3903 | 3947 | c.string_c_strError(nullptr); |
3904 | 3948 | c.string_c_strReturn(nullptr); |
3905 | 3949 | c.string_c_strParam(nullptr, 0); |
|
0 commit comments