diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2020-08-28 14:07:16 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2020-09-03 14:32:47 +0000 |
commit | 45dd074441b4477e9bd90a61c4875f70bda2feda (patch) | |
tree | 560b3263418a5c459f551d7acf77648c4c27076f | |
parent | 0ceb9f487c64b06a2298d0cff1186a3c6d0d0001 (diff) | |
download | qt-creator-45dd074441b4477e9bd90a61c4875f70bda2feda.tar.gz |
CPlusPlus: Categorize "Find Usages" results
That is, find out whether a certain access was a read, a write, a
declaration or something else, and report the result to upper layers.
Follow-up patches can make this information visible to users.
Task-number: QTCREATORBUG-12734
Task-number: QTCREATORBUG-19373
Change-Id: Iee79e39dd1eb5a986a7e27846991e0e01b2c3a2f
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
-rw-r--r-- | src/libs/cplusplus/FindUsages.cpp | 255 | ||||
-rw-r--r-- | src/libs/cplusplus/FindUsages.h | 9 | ||||
-rw-r--r-- | src/plugins/cpptools/cppfindreferences.cpp | 5 | ||||
-rw-r--r-- | tests/auto/cplusplus/findusages/tst_findusages.cpp | 314 |
4 files changed, 568 insertions, 15 deletions
diff --git a/src/libs/cplusplus/FindUsages.cpp b/src/libs/cplusplus/FindUsages.cpp index 8b976d9532..8f7a66801f 100644 --- a/src/libs/cplusplus/FindUsages.cpp +++ b/src/libs/cplusplus/FindUsages.cpp @@ -28,16 +28,21 @@ #include "Overview.h" #include <cplusplus/AST.h> -#include <cplusplus/TranslationUnit.h> +#include <cplusplus/ASTPath.h> #include <cplusplus/Control.h> -#include <cplusplus/Names.h> -#include <cplusplus/Symbols.h> #include <cplusplus/CoreTypes.h> #include <cplusplus/Literals.h> +#include <cplusplus/Names.h> #include <cplusplus/Scope.h> +#include <cplusplus/Symbols.h> +#include <cplusplus/TranslationUnit.h> +#include <cplusplus/TypeOfExpression.h> + +#include <utils/optional.h> #include <QDebug> + using namespace CPlusPlus; FindUsages::FindUsages(const QByteArray &originalSource, Document::Ptr doc, const Snapshot &snapshot) @@ -132,17 +137,251 @@ void FindUsages::reportResult(unsigned tokenIndex, const QList<LookupItem> &cand lineText = fetchLine(line); else lineText = matchingLine(tk); - - if (col) - --col; // adjust the column position. - const int len = tk.utf16chars(); - const Usage u(Utils::FilePath::fromString(_doc->fileName()), lineText, line, col, len); + const Usage u(Utils::FilePath::fromString(_doc->fileName()), lineText, + getType(line, col, tokenIndex), line, col - 1, len); _usages.append(u); _references.append(tokenIndex); } +Usage::Type FindUsages::getType(int line, int column, int tokenIndex) +{ + const auto containsToken = [tokenIndex](const AST *ast) { + return ast->firstToken() <= tokenIndex && ast->lastToken() > tokenIndex; + }; + const auto isAssignment = [this](int token) { + switch (tokenKind(token)) { + case T_AMPER_EQUAL: case T_CARET_EQUAL: case T_SLASH_EQUAL: case T_EQUAL: + case T_MINUS_EQUAL: case T_PERCENT_EQUAL: case T_PIPE_EQUAL: case T_PLUS_EQUAL: + case T_STAR_EQUAL: case T_TILDE_EQUAL: + return true; + default: + return false; + } + }; + + // This is called for the type of the LHS of an (initialization) assignment. + // We consider the RHS to be writable through the LHS if the LHS is a pointer + // that is non-const at any element level, or if it is a a non-const reference. + static const auto getUsageTypeFromDataType = [](FullySpecifiedType type) { + if (type.isAuto()) + return Usage::Type::Other; + if (const auto refType = type->asReferenceType()) + return refType->elementType().isConst() ? Usage::Type::Read : Usage::Type::WritableRef; + while (type->isPointerType()) { + type = type->asPointerType()->elementType(); + if (!type.isConst()) + return Usage::Type::WritableRef; + } + return Usage::Type::Read; + }; + + const QList<AST *> astPath = ASTPath(_doc)(line, column); + + // If we found a potential write access inside a lambda, we have to check whether the variable + // was captured by value. If so, it's not really a write access. + // FIXME: The parser does not record whether the capture was by reference. + const auto checkPotentialWrite = [&](Usage::Type usageType, auto startIt) { + if (usageType != Usage::Type::Write && usageType != Usage::Type::WritableRef) + return usageType; + for (auto it = startIt; it != astPath.rend(); ++it) { + if ((*it)->firstToken() > tokenIndex) + break; + const auto lambdaExpr = (*it)->asLambdaExpression(); + if (!lambdaExpr) + continue; + LambdaCaptureAST * const captureAst = lambdaExpr->lambda_introducer->lambda_capture; + if (!captureAst) + continue; + for (CaptureListAST *capList = captureAst->capture_list; capList; + capList = capList->next) { + if (!capList->value || !capList->value->identifier) + continue; + if (!Matcher::match(_declSymbol->name(), capList->value->identifier->name)) + continue; + return capList->value->amper_token ? usageType : Usage::Type::Read; + } + } + return usageType; + }; + + const auto getTypeOfExpr = [&](ExpressionAST *expr, auto scopeSearchPos) + -> Utils::optional<LookupItem> { + if (!expr) + return {}; + Scope *scope = nullptr; + for (auto it = scopeSearchPos; !scope && it != astPath.rend(); ++it) { + if (const auto stmt = (*it)->asCompoundStatement()) + scope = stmt->symbol; + else if (const auto klass = (*it)->asClassSpecifier()) + scope = klass->symbol; + else if (const auto ns = (*it)->asNamespace()) + scope = ns->symbol; + } + if (!scope) + scope = _doc->globalNamespace(); + const QList<LookupItem> items = typeofExpression(expr, _doc, scope); + if (items.isEmpty()) + return {}; + return Utils::optional<LookupItem>(items.first()); + }; + + const auto getUsageTypeFromLhsAndRhs + = [&](const FullySpecifiedType &lhsType, ExpressionAST *rhs, auto scopeSearchPos) { + const Usage::Type usageType = getUsageTypeFromDataType(lhsType); + if (usageType != Usage::Type::Other) + return usageType; + + // If the lhs has type auto, we use the RHS type. + const Utils::optional<LookupItem> item = getTypeOfExpr(rhs, scopeSearchPos); + if (!item) + return Usage::Type::Other; + return getUsageTypeFromDataType(item->type()); + }; + + const auto getUsageTypeForCall = [&](auto callIt) { + CallAST * const call = (*callIt)->asCall(); + + // Check whether this is a member function call on the symbol we are looking for + // (possibly indirectly via a data member). + // If it is and the function is not const, then this is a potential write. + if (call->base_expression == *(callIt - 1)) { + for (auto it = callIt; it != astPath.rbegin(); --it) { + const auto memberAccess = (*it)->asMemberAccess(); + if (!memberAccess) + continue; + if (memberAccess->member_name == *(it - 1)) + return Usage::Type::Other; + const Utils::optional<LookupItem> item + = getTypeOfExpr(memberAccess->base_expression, it); + if (!item) + return Usage::Type::Other; + FullySpecifiedType baseExprType = item->type(); + if (const auto refType = baseExprType->asReferenceType()) + baseExprType = refType->elementType(); + while (const auto ptrType = baseExprType->asPointerType()) + baseExprType = ptrType->elementType(); + Class *klass = baseExprType->asClassType(); + const LookupContext context(_doc, _snapshot); + QList<LookupItem> items; + if (!klass) { + if (const auto namedType = baseExprType->asNamedType()) { + items = context.lookup(namedType->name(), item->scope()); + if (items.isEmpty()) + return Usage::Type::Other; + klass = items.first().type()->asClassType(); + } + } + if (!klass) + return Usage::Type::Other; + items = context.lookup(memberAccess->member_name->name, klass); + if (items.isEmpty()) + return Usage::Type::Other; + for (const LookupItem &item : qAsConst(items)) { + if (item.type()->isFunctionType()) + return item.type().isConst() ? Usage::Type::Read : Usage::Type::WritableRef; + } + } + return Usage::Type::Other; + } + + // Check whether our symbol is passed as an argument to the function. + // If it is and the corresponding parameter is a non-const pointer or reference, + // then this is a potential write. + bool match = false; + int argPos = -1; + for (ExpressionListAST *argList = call->expression_list; argList && !match; + argList = argList->next, ++argPos) { + match = argList->value == *(callIt - 1); + } + if (!match) + return Usage::Type::Other; + const Utils::optional<LookupItem> item = getTypeOfExpr(call->base_expression, callIt + 1); + if (!item) + return Usage::Type::Other; + Function * const func = item->type()->asFunctionType(); + if (!func || func->argumentCount() <= argPos) + return Usage::Type::Other; + return getUsageTypeFromLhsAndRhs(func->argumentAt(argPos)->type(), + (*(callIt - 1))->asExpression(), callIt); + }; + + if (astPath.size() < 2 || !astPath.last()->asSimpleName()) + return Usage::Type::Other; + + for (auto it = astPath.rbegin() + 1; it != astPath.rend(); ++it) { + if ((*it)->asExpressionStatement()) + return Usage::Type::Read; + if ((*it)->asLambdaCapture() || (*it)->asNamedTypeSpecifier() + || (*it)->asElaboratedTypeSpecifier()) { + return Usage::Type::Other; + } + if ((*it)->asTypenameTypeParameter()) + return Usage::Type::Declaration; + if (ClassSpecifierAST *classSpec = (*it)->asClassSpecifier()) { + if (classSpec->name == *(it - 1)) + return Usage::Type::Declaration; + continue; + } + if ((*it)->asCall()) + return checkPotentialWrite(getUsageTypeForCall(it), it + 1); + if (const auto binExpr = (*it)->asBinaryExpression()) { + if (binExpr->left_expression == *(it - 1) && isAssignment(binExpr->binary_op_token)) + return checkPotentialWrite(Usage::Type::Write, it + 1); + const Utils::optional<LookupItem> item = getTypeOfExpr(binExpr->left_expression, it + 1); + if (!item) + return Usage::Type::Other; + return checkPotentialWrite(getUsageTypeFromLhsAndRhs( + item->type(), binExpr->right_expression, it), it + 1); + } + if (const auto unaryOp = (*it)->asUnaryExpression()) { + switch (tokenKind(unaryOp->unary_op_token)) { + case T_PLUS_PLUS: case T_MINUS_MINUS: + return checkPotentialWrite(Usage::Type::Write, it + 1); + case T_AMPER: case T_STAR: + continue; + default: + return Usage::Type::Read; + } + } + if (const auto declaratorId = (*it)->asDeclaratorId()) { + // We don't want to classify constructors and destructors as declarations + // when listing class usages. + if (_declSymbol->asClass()) + return Usage::Type::Other; + continue; + } + if (const auto declarator = (*it)->asDeclarator()) { + if (containsToken(declarator->core_declarator)) + return Usage::Type::Declaration; + if (const auto decl = (*(it + 1))->asSimpleDeclaration()) { + if (decl->symbols && decl->symbols->value) { + return checkPotentialWrite( + getUsageTypeFromLhsAndRhs(decl->symbols->value->type(), + declarator->initializer, it + 1), it + 1); + } + } + return Usage::Type::Other; + } + if (const auto retStmt = (*it)->asReturnStatement()) { + for (auto funcIt = it + 1; funcIt != astPath.rend(); ++funcIt) { + if (FunctionDefinitionAST * const funcAst = (*funcIt)->asFunctionDefinition()) { + if (funcAst->symbol) { + return checkPotentialWrite( + getUsageTypeFromLhsAndRhs(funcAst->symbol->type(), + retStmt->expression, funcIt), + funcIt + 1); + } + } + } + return Usage::Type::Other; + } + } + + return Usage::Type::Other; +} + QString FindUsages::matchingLine(const Token &tk) const { const char *beg = _source.constData(); diff --git a/src/libs/cplusplus/FindUsages.h b/src/libs/cplusplus/FindUsages.h index e0b37b12a8..a0e1130c15 100644 --- a/src/libs/cplusplus/FindUsages.h +++ b/src/libs/cplusplus/FindUsages.h @@ -39,13 +39,15 @@ namespace CPlusPlus { class CPLUSPLUS_EXPORT Usage { public: + enum class Type { Declaration, Read, Write, WritableRef, Other }; + Usage() = default; - Usage(const Utils::FilePath &path, const QString &lineText, int line, int col, int len) - : path(path), lineText(lineText), line(line), col(col), len(len) {} + Usage(const Utils::FilePath &path, const QString &lineText, Type t, int line, int col, int len) + : path(path), lineText(lineText), type(t), line(line), col(col), len(len) {} -public: Utils::FilePath path; QString lineText; + Type type = Type::Other; int line = 0; int col = 0; int len = 0; @@ -71,6 +73,7 @@ protected: void reportResult(unsigned tokenIndex, const Name *name, Scope *scope = nullptr); void reportResult(unsigned tokenIndex, const QList<LookupItem> &candidates); + Usage::Type getType(int line, int column, int tokenIndex); bool checkCandidates(const QList<LookupItem> &candidates) const; void checkExpression(unsigned startToken, unsigned endToken, Scope *scope = nullptr); diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp index 131d0fca94..1f567bed7e 100644 --- a/src/plugins/cpptools/cppfindreferences.cpp +++ b/src/plugins/cpptools/cppfindreferences.cpp @@ -650,8 +650,9 @@ restart_search: if (macro.name() == useMacro.name()) { unsigned column; const QString &lineSource = matchingLine(use.bytesBegin(), source, &column); - usages.append(CPlusPlus::Usage(fileName, lineSource, use.beginLine(), column, - useMacro.nameToQString().size())); + usages.append(CPlusPlus::Usage(fileName, lineSource, + CPlusPlus::Usage::Type::Other, use.beginLine(), + column, useMacro.nameToQString().size())); } } } diff --git a/tests/auto/cplusplus/findusages/tst_findusages.cpp b/tests/auto/cplusplus/findusages/tst_findusages.cpp index 2dd9566021..3a88bf2a2e 100644 --- a/tests/auto/cplusplus/findusages/tst_findusages.cpp +++ b/tests/auto/cplusplus/findusages/tst_findusages.cpp @@ -133,6 +133,7 @@ private Q_SLOTS: void variableTemplateInExpression(); void variadicMacros(); + void writableRefs(); }; void tst_FindUsages::dump(const QList<Usage> &usages) const @@ -181,6 +182,8 @@ void tst_FindUsages::inlineMethod() FindUsages findUsages(src, doc, snapshot); findUsages(arg); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read); QCOMPARE(findUsages.references().size(), 2); } @@ -214,6 +217,9 @@ void tst_FindUsages::lambdaCaptureByValue() FindUsages findUsages(src, doc, snapshot); findUsages(d); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Read); } void tst_FindUsages::lambdaCaptureByReference() @@ -246,6 +252,10 @@ void tst_FindUsages::lambdaCaptureByReference() FindUsages findUsages(src, doc, snapshot); findUsages(d); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QEXPECT_FAIL(nullptr, "parser does not record capture type", Continue); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Write); } void tst_FindUsages::shadowedNames_1() @@ -276,6 +286,8 @@ void tst_FindUsages::shadowedNames_1() FindUsages findUsages(src, doc, snapshot); findUsages(d); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); } void tst_FindUsages::shadowedNames_2() @@ -309,6 +321,9 @@ void tst_FindUsages::shadowedNames_2() FindUsages findUsages(src, doc, snapshot); findUsages(d); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); } void tst_FindUsages::staticVariables() @@ -355,6 +370,11 @@ void tst_FindUsages::staticVariables() FindUsages findUsages(src, doc, snapshot); findUsages(d); QCOMPARE(findUsages.usages().size(), 5); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Write); + QCOMPARE(findUsages.usages().at(3).type, Usage::Type::Write); + QCOMPARE(findUsages.usages().at(4).type, Usage::Type::Write); } void tst_FindUsages::functionNameFoundInArguments() @@ -387,12 +407,15 @@ void foo2(int b=bar()){} // 3rd result QCOMPARE(find.usages().size(), 3); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 1); QCOMPARE(find.usages()[0].col, 5); + QCOMPARE(find.usages()[1].type, Usage::Type::Other); QCOMPARE(find.usages()[1].line, 4); QCOMPARE(find.usages()[1].col, 16); + QCOMPARE(find.usages()[2].type, Usage::Type::Other); QCOMPARE(find.usages()[2].line, 5); QCOMPARE(find.usages()[2].col, 16); } @@ -451,18 +474,22 @@ struct Struct{ find(memberFunctionFoo); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 3); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Other); QCOMPARE(find.usages()[1].line, 5); QCOMPARE(find.usages()[1].col, 24); find(variableFoo); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 5); QCOMPARE(find.usages()[0].col, 12); + QCOMPARE(find.usages()[1].type, Usage::Type::Read); QCOMPARE(find.usages()[1].line, 6); QCOMPARE(find.usages()[1].col, 22); } @@ -519,6 +546,13 @@ int main() { FindUsages find(src, doc, snapshot); find(sv); QCOMPARE(find.usages().size(), 7); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); + QCOMPARE(find.usages().at(2).type, Usage::Type::Read); + QCOMPARE(find.usages().at(3).type, Usage::Type::Read); + QCOMPARE(find.usages().at(4).type, Usage::Type::Read); + QCOMPARE(find.usages().at(5).type, Usage::Type::Read); + QCOMPARE(find.usages().at(6).type, Usage::Type::Read); } void tst_FindUsages::templateConstructorVsCallOperator() @@ -571,6 +605,13 @@ int main() FindUsages find(src, doc, snapshot); find(sv); QCOMPARE(find.usages().size(), 7); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); + QCOMPARE(find.usages().at(2).type, Usage::Type::Read); + QCOMPARE(find.usages().at(3).type, Usage::Type::Read); + QCOMPARE(find.usages().at(4).type, Usage::Type::Read); + QCOMPARE(find.usages().at(5).type, Usage::Type::Read); + QCOMPARE(find.usages().at(6).type, Usage::Type::Read); } #if 0 @@ -667,6 +708,8 @@ void tst_FindUsages::qproperty_1() FindUsages findUsages(src, doc, snapshot); findUsages(setX_method); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Declaration); QCOMPARE(findUsages.references().size(), 2); } @@ -711,6 +754,8 @@ void tst_FindUsages::instantiateTemplateWithNestedClass() FindUsages findUsages(src, doc, snapshot); findUsages(barDeclaration); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::operatorAsteriskOfNestedClassOfTemplateClass_QTCREATORBUG9006() @@ -756,6 +801,8 @@ void tst_FindUsages::operatorAsteriskOfNestedClassOfTemplateClass_QTCREATORBUG90 findUsages(fooDeclaration); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::anonymousClass_QTCREATORBUG8963() @@ -799,6 +846,8 @@ void tst_FindUsages::anonymousClass_QTCREATORBUG8963() findUsages(isNotIntDeclaration); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::anonymousClass_QTCREATORBUG11859() @@ -839,9 +888,13 @@ void tst_FindUsages::anonymousClass_QTCREATORBUG11859() FindUsages findUsages(src, doc, snapshot); findUsages(fooAsStruct); QCOMPARE(findUsages.references().size(), 1); + QCOMPARE(findUsages.usages().size(), 1); + QCOMPARE(findUsages.usages().first().type, Usage::Type::Declaration); findUsages(fooAsMemberOfAnonymousStruct); QCOMPARE(findUsages.references().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::using_insideGlobalNamespace() @@ -882,6 +935,9 @@ void tst_FindUsages::using_insideGlobalNamespace() findUsages(structSymbol); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); } void tst_FindUsages::using_insideNamespace() @@ -925,6 +981,9 @@ void tst_FindUsages::using_insideNamespace() findUsages(structSymbol); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); } void tst_FindUsages::using_insideFunction() @@ -965,6 +1024,9 @@ void tst_FindUsages::using_insideFunction() findUsages(structSymbol); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); } void tst_FindUsages::operatorArrowOfNestedClassOfTemplateClass_QTCREATORBUG9005() @@ -1009,6 +1071,8 @@ void tst_FindUsages::operatorArrowOfNestedClassOfTemplateClass_QTCREATORBUG9005( FindUsages findUsages(src, doc, snapshot); findUsages(fooDeclaration); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::templateClassParameters() @@ -1044,6 +1108,11 @@ void tst_FindUsages::templateClassParameters() FindUsages findUsages(src, doc, snapshot); findUsages(templArgument); QCOMPARE(findUsages.usages().size(), 5); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(3).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(4).type, Usage::Type::Other); } void tst_FindUsages::templateClass_className() @@ -1085,6 +1154,13 @@ void tst_FindUsages::templateClass_className() FindUsages findUsages(src, doc, snapshot); findUsages(classTS); QCOMPARE(findUsages.usages().size(), 7); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(3).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(4).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(5).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(6).type, Usage::Type::Other); } void tst_FindUsages::templateFunctionParameters() @@ -1118,6 +1194,10 @@ void tst_FindUsages::templateFunctionParameters() FindUsages findUsages(src, doc, snapshot); findUsages(templArgument); QCOMPARE(findUsages.usages().size(), 4); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(3).type, Usage::Type::Other); } void tst_FindUsages::templatedFunction_QTCREATORBUG9749() @@ -1148,6 +1228,8 @@ void tst_FindUsages::templatedFunction_QTCREATORBUG9749() FindUsages findUsages(src, doc, snapshot); findUsages(func); QCOMPARE(findUsages.usages().size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); } void tst_FindUsages::usingInDifferentNamespace_QTCREATORBUG7978() @@ -1187,6 +1269,9 @@ void tst_FindUsages::usingInDifferentNamespace_QTCREATORBUG7978() FindUsages findUsages(src, doc, snapshot); findUsages(templateClass); QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Other); + QCOMPARE(findUsages.usages().at(2).type, Usage::Type::Other); } void tst_FindUsages::unicodeIdentifier() @@ -1214,6 +1299,8 @@ void tst_FindUsages::unicodeIdentifier() findUsages(declaration); const QList<Usage> usages = findUsages.usages(); QCOMPARE(usages.size(), 2); + QCOMPARE(findUsages.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(findUsages.usages().at(1).type, Usage::Type::Write); QCOMPARE(usages.at(0).len, 7); QCOMPARE(usages.at(1).len, 7); } @@ -1243,8 +1330,10 @@ void tst_FindUsages::inAlignas() FindUsages find(src, doc, snapshot); find(c); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 1); QCOMPARE(find.usages()[0].col, 7); + QCOMPARE(find.usages()[1].type, Usage::Type::Other); QCOMPARE(find.usages()[1].line, 2); QCOMPARE(find.usages()[1].col, 15); } @@ -1284,8 +1373,10 @@ void tst_FindUsages::memberAccessAsTemplate() FindUsages find(src, doc, snapshot); find(c); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 1); QCOMPARE(find.usages()[0].col, 7); + QCOMPARE(find.usages()[1].type, Usage::Type::Other); QCOMPARE(find.usages()[1].line, 11); QCOMPARE(find.usages()[1].col, 24); } @@ -1303,8 +1394,10 @@ void tst_FindUsages::memberAccessAsTemplate() FindUsages find(src, doc, snapshot); find(f); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 4); QCOMPARE(find.usages()[0].col, 7); + QCOMPARE(find.usages()[1].type, Usage::Type::Other); QCOMPARE(find.usages()[1].line, 11); QCOMPARE(find.usages()[1].col, 11); } @@ -1344,6 +1437,10 @@ void tst_FindUsages::variadicFunctionTemplate() FindUsages find(src, doc, snapshot); find(v); QCOMPARE(find.usages().size(), 4); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); + QCOMPARE(find.usages().at(2).type, Usage::Type::Read); + QCOMPARE(find.usages().at(3).type, Usage::Type::Read); } } @@ -1391,8 +1488,13 @@ void tst_FindUsages::typeTemplateParameterWithDefault() FindUsages find(src, doc, snapshot); find(xv); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); find(sv); QCOMPARE(find.usages().size(), 3); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); + QCOMPARE(find.usages().at(2).type, Usage::Type::Read); } } @@ -1430,6 +1532,8 @@ void tst_FindUsages::resolveOrder_for_templateFunction_vs_function() FindUsages find(src, doc, snapshot); find(xv); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); } } @@ -1470,6 +1574,9 @@ void tst_FindUsages::templateArrowOperator_with_defaultType() FindUsages find(src, doc, snapshot); find(sv); QCOMPARE(find.usages().size(), 3); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); + QCOMPARE(find.usages().at(2).type, Usage::Type::Read); } } @@ -1489,7 +1596,7 @@ void tst_FindUsages::templateSpecialization_with_IntArgument() " S<2> s2;\n" " s0.s.value;\n" " s1.s.value;\n" - " s2.s.value;\n" + " s2.s.value = s2.s.value;\n" "}\n"; Document::Ptr doc = Document::create("templateSpecialization_with_IntArgument"); @@ -1549,18 +1656,25 @@ void tst_FindUsages::templateSpecialization_with_IntArgument() find(sv[1]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 2); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Read); QCOMPARE(find.usages()[1].line, 13); QCOMPARE(find.usages()[1].col, 9); find(sv[2]); - QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().size(), 3); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 3); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Write); QCOMPARE(find.usages()[1].line, 14); QCOMPARE(find.usages()[1].col, 9); + QCOMPARE(find.usages()[2].type, Usage::Type::Read); + QCOMPARE(find.usages()[2].line, 14); + QCOMPARE(find.usages()[2].col, 22); } } @@ -1621,16 +1735,20 @@ void tst_FindUsages::templateSpecialization_with_BoolArgument() find(sv[0]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 1); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Read); QCOMPARE(find.usages()[1].line, 9); QCOMPARE(find.usages()[1].col, 9); find(sv[1]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 2); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Read); QCOMPARE(find.usages()[1].line, 10); QCOMPARE(find.usages()[1].col, 9); } @@ -1693,16 +1811,20 @@ void tst_FindUsages::templatePartialSpecialization() find(sv[0]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 1); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Read); QCOMPARE(find.usages()[1].line, 9); QCOMPARE(find.usages()[1].col, 10); find(sv[1]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages()[0].type, Usage::Type::Declaration); QCOMPARE(find.usages()[0].line, 2); QCOMPARE(find.usages()[0].col, 15); + QCOMPARE(find.usages()[1].type, Usage::Type::Read); QCOMPARE(find.usages()[1].line, 10); QCOMPARE(find.usages()[1].col, 10); } @@ -1758,12 +1880,18 @@ int main() find(sv[0]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); find(sv[1]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); find(sv[2]); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::template_SFINAE_1() @@ -1802,6 +1930,8 @@ int main(){ FindUsages find(src, doc, snapshot); find(sv); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::variableTemplateInExpression() @@ -1845,6 +1975,8 @@ int main(){ FindUsages find(src, doc, snapshot); find(sv); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); } void tst_FindUsages::variadicMacros() @@ -1883,6 +2015,184 @@ int main(){} FindUsages find(src, doc, snapshot); find(sv); QCOMPARE(find.usages().size(), 2); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Read); +} + +void tst_FindUsages::writableRefs() +{ + const QByteArray src = R"( +struct S { + static int value; + static void *p; + static const void *p2; + struct Nested { + int constFunc() const; + void nonConstFunc(); + } n; + Nested constFunc() const; + void nonConstFunc(); +}; +void func1(int &); +void func2(const int &); +void func3(int *); +void func4(const int *); +void func5(int); +int main() +{ + S s; + auto *p = &s.value; + int **pp; + p = &s.value; + *pp = &s.value; + //p = &s::value; FIXME: This one is not found at all... + s.p = &s.value; + // s::p = &s::value; FIXME: Same here. + (&s)->p = &((new S)->value); + const int *p2 = &s.value; + s.p2 = &s.value; + int * const p3 = &s.value; + int &r = s.value; + const int &cr = s.value; + func1(s.value); + func2(s.value); + func3(&s.value); + func4(&s.value); + func5(s.value); + *p = 5; + func1(*p); + func2(*p); + func3(p); + func4(p); + func5(p); + int &r2 = *p; + const int &cr2 = *p; + s = S(); + auto * const ps = &s; + const auto *ps2 = &s; + auto &pr = s; + const auto pr2 = &s; + s.constFunc().nonConstFunc(); + s.nonConstFunc(); + (&s)->nonConstFunc(); + s.n.constFunc(); + s.n.nonConstFunc(); +} +)"; + + const Document::Ptr doc = Document::create("writableRefs"); + doc->setUtf8Source(src); + doc->check(); + + QVERIFY(doc->diagnosticMessages().isEmpty()); + QCOMPARE(doc->globalSymbolCount(), 7); + + Snapshot snapshot; + snapshot.insert(doc); + + Class * const structS = doc->globalSymbolAt(0)->asClass(); + QVERIFY(structS); + QCOMPARE(structS->name()->identifier()->chars(), "S"); + QCOMPARE(structS->memberCount(), 7); + + Declaration * const sv = structS->memberAt(0)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "value"); + + // Access to struct member + FindUsages find(src, doc, snapshot); + find(sv); + QCOMPARE(find.usages().size(), 16); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(2).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(3).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(4).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(5).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(6).type, Usage::Type::Read); + QCOMPARE(find.usages().at(7).type, Usage::Type::Read); + QCOMPARE(find.usages().at(8).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(9).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(10).type, Usage::Type::Read); + QCOMPARE(find.usages().at(11).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(12).type, Usage::Type::Read); + QCOMPARE(find.usages().at(13).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(14).type, Usage::Type::Read); + QCOMPARE(find.usages().at(15).type, Usage::Type::Read); + + Function * const main = doc->globalSymbolAt(6)->asFunction(); + QVERIFY(main); + QCOMPARE(main->memberCount(), 1); + Block * const block = main->memberAt(0)->asBlock(); + QVERIFY(block); + QCOMPARE(block->memberCount(), 13); + + // Access to pointer + Declaration * const p = block->memberAt(1)->asDeclaration(); + QVERIFY(p); + QCOMPARE(p->name()->identifier()->chars(), "p"); + find(p); + QCOMPARE(find.usages().size(), 10); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Write); + QCOMPARE(find.usages().at(2).type, Usage::Type::Write); + QCOMPARE(find.usages().at(3).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(4).type, Usage::Type::Read); + QCOMPARE(find.usages().at(5).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(6).type, Usage::Type::Read); + QCOMPARE(find.usages().at(7).type, Usage::Type::Read); + QCOMPARE(find.usages().at(8).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(9).type, Usage::Type::Read); + + // Access to struct variable via its members + Declaration * const varS = block->memberAt(0)->asDeclaration(); + QVERIFY(varS); + QCOMPARE(varS->name()->identifier()->chars(), "s"); + find(varS); + QCOMPARE(find.usages().size(), 28); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(2).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(3).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(4).type, Usage::Type::Write); + QCOMPARE(find.usages().at(5).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(6).type, Usage::Type::Write); + QCOMPARE(find.usages().at(7).type, Usage::Type::Read); + QCOMPARE(find.usages().at(8).type, Usage::Type::Write); + QCOMPARE(find.usages().at(9).type, Usage::Type::Read); + QCOMPARE(find.usages().at(10).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(11).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(12).type, Usage::Type::Read); + QCOMPARE(find.usages().at(13).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(14).type, Usage::Type::Read); + QCOMPARE(find.usages().at(15).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(16).type, Usage::Type::Read); + QCOMPARE(find.usages().at(17).type, Usage::Type::Read); + + // Direct access to struct variable + QCOMPARE(find.usages().at(18).type, Usage::Type::Write); + QCOMPARE(find.usages().at(19).type, Usage::Type::WritableRef); + QEXPECT_FAIL(nullptr, "parser does not record const qualifier for auto types", Continue); + QCOMPARE(find.usages().at(20).type, Usage::Type::Read); + QEXPECT_FAIL(nullptr, "parser does not record reference qualifier for auto types", Continue); + QCOMPARE(find.usages().at(21).type, Usage::Type::WritableRef); + QEXPECT_FAIL(nullptr, "parser does not record const qualifier for auto types", Continue); + QCOMPARE(find.usages().at(22).type, Usage::Type::Read); + + // Member function calls. + QCOMPARE(find.usages().at(23).type, Usage::Type::Read); + QCOMPARE(find.usages().at(24).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(25).type, Usage::Type::WritableRef); + QCOMPARE(find.usages().at(26).type, Usage::Type::Read); + QCOMPARE(find.usages().at(27).type, Usage::Type::WritableRef); + + // Usages of struct type + find(structS); + QCOMPARE(find.usages().size(), 4); + QCOMPARE(find.usages().at(0).type, Usage::Type::Declaration); + QCOMPARE(find.usages().at(1).type, Usage::Type::Other); + QCOMPARE(find.usages().at(2).type, Usage::Type::Other); + QCOMPARE(find.usages().at(3).type, Usage::Type::Other); } QTEST_APPLESS_MAIN(tst_FindUsages) |