Skip to content

Commit 8668d44

Browse files
authored
Add support for string_view (danmar#3480)
1 parent 7180904 commit 8668d44

File tree

12 files changed

+253
-13
lines changed

12 files changed

+253
-13
lines changed

cfg/boost.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
<container id="boostVector" startPattern="boost :: vector|small_vector &lt;" inherits="stdVector"/>
8282
<container id="boostStableVector" startPattern="boost :: stable_vector|static_vector &lt;" inherits="stdVectorDeque"/>
8383
<container id="boostDeque" startPattern="boost :: deque &lt;" inherits="stdDeque"/>
84+
<container id="boostStringView" startPattern="boost :: string_view" inherits="stdStringView"/>
8485
<!-- ########## Boost smart pointers ########## -->
8586
<!-- https://www.boost.org/doc/libs/1_70_0/libs/smart_ptr/doc/html/smart_ptr.html -->
8687
<smart-pointer class-name="boost::scoped_ptr">

cfg/cppcheck-cfg.rng

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@
416416
<optional>
417417
<attribute name="hasInitializerListConstructor"><ref name="DATA-BOOL"/></attribute>
418418
</optional>
419+
<optional>
420+
<attribute name="view"><ref name="DATA-BOOL"/></attribute>
421+
</optional>
419422
<optional>
420423
<attribute name="itEndPattern"><text/></attribute>
421424
</optional>

cfg/std.cfg

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8305,6 +8305,18 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
83058305
<type templateParameter="0"/>
83068306
</container>
83078307
<container id="stdString" startPattern="std :: string|wstring|u16string|u32string" endPattern="" inherits="stdAllString"/>
8308+
<container id="stdAllStringView" inherits="stdAllString" view="true">
8309+
<size>
8310+
<function name="remove_prefix" action="change"/>
8311+
<function name="remove_suffix" action="change"/>
8312+
</size>
8313+
</container>
8314+
<container id="stdBasicStringView" startPattern="std :: basic_string_view &lt;" inherits="stdAllStringView">
8315+
<type templateParameter="0"/>
8316+
</container>
8317+
<container id="stdStringView" startPattern="std :: string_view|wstring_view|u16string_view|u32string_view" endPattern="" inherits="stdAllStringView"/>
8318+
<container id="stdExperimentalStringView" startPattern="std :: experimental :: string_view|wstring_view|u16string_view|u32string_view" endPattern="" inherits="stdAllStringView"/>
8319+
<container id="stdExperimentalBasicStringView" startPattern="std :: experimental :: basic_string_view &lt;" inherits="stdBasicStringView" />
83088320
<smart-pointer class-name="std::auto_ptr">
83098321
<unique/>
83108322
</smart-pointer>

lib/astutils.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,18 @@ bool astIsIterator(const Token *tok)
249249
return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::ITERATOR;
250250
}
251251

252-
bool astIsContainer(const Token *tok)
252+
bool astIsContainer(const Token* tok) {
253+
return getLibraryContainer(tok) != nullptr && !astIsIterator(tok);
254+
}
255+
256+
bool astIsContainerView(const Token* tok)
253257
{
254-
return getLibraryContainer(tok) != nullptr && tok->valueType()->type != ValueType::Type::ITERATOR;
258+
const Library::Container* container = getLibraryContainer(tok);
259+
return container && !astIsIterator(tok) && container->view;
260+
}
261+
262+
bool astIsContainerOwned(const Token* tok) {
263+
return astIsContainer(tok) && !astIsContainerView(tok);
255264
}
256265

257266
std::string astCanonicalType(const Token *expr)

lib/astutils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ bool astIsIterator(const Token *tok);
8585

8686
bool astIsContainer(const Token *tok);
8787

88+
bool astIsContainerView(const Token* tok);
89+
bool astIsContainerOwned(const Token* tok);
90+
8891
/**
8992
* Get canonical type of expression. const/static/etc are not included and neither *&.
9093
* For example:

lib/checkautovariables.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -558,14 +558,13 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
558558
break;
559559
}
560560
}
561-
562561
}
562+
const bool escape = Token::Match(tok->astParent(), "return|throw");
563563
for (const ValueFlow::Value& val:tok->values()) {
564564
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
565565
continue;
566566
if (!printInconclusive && val.isInconclusive())
567567
continue;
568-
const bool escape = Token::Match(tok->astParent(), "return|throw");
569568
for (const LifetimeToken& lt :
570569
getLifetimeTokens(getParentLifetime(val.tokvalue), escape || isAssignedToNonLocal(tok))) {
571570
const Token * tokvalue = lt.token;
@@ -575,7 +574,8 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
575574
continue;
576575
if (!isLifetimeBorrowed(tok, mSettings))
577576
continue;
578-
if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()))
577+
if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()) &&
578+
!astIsContainerView(tok->astParent()))
579579
continue;
580580
if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) &&
581581
isInScope(tokvalue->variable()->nameToken(), scope)) ||

lib/library.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
439439
const char* const hasInitializerListConstructor = node->Attribute("hasInitializerListConstructor");
440440
if (hasInitializerListConstructor)
441441
container.hasInitializerListConstructor = std::string(hasInitializerListConstructor) == "true";
442+
const char* const view = node->Attribute("view");
443+
if (view)
444+
container.view = std::string(view) == "true";
442445

443446
for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) {
444447
const std::string containerNodeName = containerNode->Name();

lib/library.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,23 +204,42 @@ class CPPCHECKLIB Library {
204204

205205
class Container {
206206
public:
207-
Container() :
208-
type_templateArgNo(-1),
207+
Container()
208+
: type_templateArgNo(-1),
209209
size_templateArgNo(-1),
210210
arrayLike_indexOp(false),
211211
stdStringLike(false),
212212
stdAssociativeLike(false),
213213
opLessAllowed(true),
214214
hasInitializerListConstructor(false),
215215
unstableErase(false),
216-
unstableInsert(false) {}
216+
unstableInsert(false),
217+
view(false)
218+
{}
217219

218220
enum class Action {
219-
RESIZE, CLEAR, PUSH, POP, FIND, INSERT, ERASE, CHANGE_CONTENT, CHANGE, CHANGE_INTERNAL,
221+
RESIZE,
222+
CLEAR,
223+
PUSH,
224+
POP,
225+
FIND,
226+
INSERT,
227+
ERASE,
228+
CHANGE_CONTENT,
229+
CHANGE,
230+
CHANGE_INTERNAL,
220231
NO_ACTION
221232
};
222233
enum class Yield {
223-
AT_INDEX, ITEM, BUFFER, BUFFER_NT, START_ITERATOR, END_ITERATOR, ITERATOR, SIZE, EMPTY,
234+
AT_INDEX,
235+
ITEM,
236+
BUFFER,
237+
BUFFER_NT,
238+
START_ITERATOR,
239+
END_ITERATOR,
240+
ITERATOR,
241+
SIZE,
242+
EMPTY,
224243
NO_YIELD
225244
};
226245
struct Function {
@@ -238,6 +257,7 @@ class CPPCHECKLIB Library {
238257
bool hasInitializerListConstructor;
239258
bool unstableErase;
240259
bool unstableInsert;
260+
bool view;
241261

242262
Action getAction(const std::string& function) const {
243263
const std::map<std::string, Function>::const_iterator i = functions.find(function);

lib/valueflow.cpp

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,22 +3031,32 @@ static bool isNotLifetimeValue(const ValueFlow::Value& val)
30313031
return !val.isLifetimeValue();
30323032
}
30333033

3034+
static bool isLifetimeOwned(const ValueType* vtParent)
3035+
{
3036+
if (vtParent->container)
3037+
return !vtParent->container->view;
3038+
return vtParent->type == ValueType::CONTAINER;
3039+
}
3040+
30343041
static bool isLifetimeOwned(const ValueType *vt, const ValueType *vtParent)
30353042
{
30363043
if (!vtParent)
30373044
return false;
30383045
if (!vt) {
3039-
if (vtParent->type == ValueType::CONTAINER)
3046+
if (isLifetimeOwned(vtParent))
30403047
return true;
30413048
return false;
30423049
}
3050+
// If converted from iterator to pointer then the iterator is most likely a pointer
3051+
if (vtParent->pointer == 1 && vt->pointer == 0 && vt->type == ValueType::ITERATOR)
3052+
return false;
30433053
if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE) {
30443054
if (vt->pointer != vtParent->pointer)
30453055
return true;
30463056
if (vt->type != vtParent->type) {
30473057
if (vtParent->type == ValueType::RECORD)
30483058
return true;
3049-
if (vtParent->type == ValueType::CONTAINER)
3059+
if (isLifetimeOwned(vtParent))
30503060
return true;
30513061
}
30523062
}
@@ -3062,6 +3072,8 @@ static bool isLifetimeBorrowed(const ValueType *vt, const ValueType *vtParent)
30623072
return false;
30633073
if (vt->pointer > 0 && vt->pointer == vtParent->pointer)
30643074
return true;
3075+
if (vtParent->container && vtParent->container->view)
3076+
return true;
30653077
if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE && vtParent->container == vt->container) {
30663078
if (vtParent->pointer > vt->pointer)
30673079
return true;
@@ -3146,6 +3158,42 @@ static bool isDifferentType(const Token* src, const Token* dst)
31463158
return false;
31473159
}
31483160

3161+
static std::vector<ValueType> getParentValueTypes(const Token* tok, const Settings* settings = nullptr)
3162+
{
3163+
if (!tok)
3164+
return {};
3165+
if (!tok->astParent())
3166+
return {};
3167+
if (Token::Match(tok->astParent(), "(|{|,")) {
3168+
int argn = -1;
3169+
const Token* ftok = getTokenArgumentFunction(tok, argn);
3170+
if (ftok && ftok->function()) {
3171+
std::vector<ValueType> result;
3172+
std::vector<const Variable*> argsVars = getArgumentVars(ftok, argn);
3173+
for (const Variable* var : getArgumentVars(ftok, argn)) {
3174+
if (!var)
3175+
continue;
3176+
if (!var->valueType())
3177+
continue;
3178+
result.push_back(*var->valueType());
3179+
}
3180+
return result;
3181+
}
3182+
}
3183+
if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") &&
3184+
astIsContainer(tok->astParent()->tokAt(-2)->astOperand1())) {
3185+
const Token* contTok = tok->astParent()->tokAt(-2)->astOperand1();
3186+
const ValueType* vtCont = contTok->valueType();
3187+
if (!vtCont->containerTypeToken)
3188+
return {};
3189+
ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings);
3190+
return {std::move(vtParent)};
3191+
}
3192+
if (tok->astParent()->valueType())
3193+
return {*tok->astParent()->valueType()};
3194+
return {};
3195+
}
3196+
31493197
bool isLifetimeBorrowed(const Token *tok, const Settings *settings)
31503198
{
31513199
if (!tok)
@@ -3605,6 +3653,13 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
36053653
}
36063654
if (update)
36073655
valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings);
3656+
} else if (tok->valueType()) {
3657+
// TODO: Propagate lifetimes with library functions
3658+
if (settings->library.getFunction(tok->previous()))
3659+
return;
3660+
// Assume constructing the valueType
3661+
valueFlowLifetimeConstructor(tok, tokenlist, errorLogger, settings);
3662+
valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings);
36083663
}
36093664
}
36103665

@@ -3673,7 +3728,13 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error
36733728
Token* parent = tok->astParent();
36743729
while (Token::simpleMatch(parent, ","))
36753730
parent = parent->astParent();
3676-
if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) {
3731+
if (Token::Match(tok, "{|(") && astIsContainerView(tok) && !tok->function()) {
3732+
std::vector<const Token*> args = getArguments(tok);
3733+
if (args.size() == 1 && astIsContainerOwned(args.front())) {
3734+
LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject}.byRef(
3735+
tok, tokenlist, errorLogger, settings);
3736+
}
3737+
} else if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) {
36773738
valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings);
36783739
} else if (Token::simpleMatch(tok, "{") && hasInitList(parent)) {
36793740
std::vector<const Token *> args = getArguments(tok);
@@ -3764,6 +3825,16 @@ static bool isDecayedPointer(const Token *tok)
37643825
return astIsPointer(tok->astParent());
37653826
}
37663827

3828+
static bool isConvertedToView(const Token* tok, const Settings* settings)
3829+
{
3830+
std::vector<ValueType> vtParents = getParentValueTypes(tok, settings);
3831+
return std::any_of(vtParents.begin(), vtParents.end(), [&](const ValueType& vt) {
3832+
if (!vt.container)
3833+
return false;
3834+
return vt.container->view;
3835+
});
3836+
}
3837+
37673838
static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings)
37683839
{
37693840
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
@@ -3861,6 +3932,13 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
38613932
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
38623933
}
38633934
}
3935+
// Converting to container view
3936+
else if (astIsContainerOwned(tok) && isConvertedToView(tok, settings)) {
3937+
LifetimeStore ls =
3938+
LifetimeStore{tok, "Converted to container view", ValueFlow::Value::LifetimeKind::SubObject};
3939+
ls.byRef(tok, tokenlist, errorLogger, settings);
3940+
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
3941+
}
38643942
// container lifetimes
38653943
else if (astIsContainer(tok)) {
38663944
Token * parent = astParentSkipParens(tok);
@@ -3902,6 +3980,16 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
39023980
continue;
39033981
toks.push_back(v.tokvalue);
39043982
}
3983+
} else if (astIsContainerView(tok)) {
3984+
for (const ValueFlow::Value& v : tok->values()) {
3985+
if (!v.isLifetimeValue())
3986+
continue;
3987+
if (!v.tokvalue)
3988+
continue;
3989+
if (!astIsContainerOwned(v.tokvalue))
3990+
continue;
3991+
toks.push_back(v.tokvalue);
3992+
}
39053993
} else {
39063994
toks = {tok};
39073995
}

man/reference-cfg-format.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,8 @@ A lot of C++ libraries, among those the STL itself, provide containers with very
580580

581581
The `hasInitializerListConstructor` attribute can be set when the container has a constructor taking an initializer list.
582582

583+
The `view` attribute can be set when the container is a view, which means it borrows the lifetime of another container.
584+
583585
Inside the `<container>` tag, functions can be defined inside of the tags `<size>`, `<access>` and `<other>` (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator".
584586

585587
The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown):

0 commit comments

Comments
 (0)