Fixed #11700 (Markup support is broken, unused function false positives) (#5025)

This commit is contained in:
Daniel Marjamäki 2023-05-03 11:19:13 +02:00 committed by GitHub
parent ec2a2ad41f
commit 46b9d4ec61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 322 additions and 29 deletions

View File

@ -112,6 +112,11 @@ bool CppCheckExecutor::parseFromArgs(Settings &settings, int argc, const char* c
return false;
}
// Libraries must be loaded before FileLister is executed to ensure markup files will be
// listed properly.
if (!loadLibraries(settings))
return false;
// Check that all include paths exist
{
for (std::list<std::string>::iterator iter = settings.includePaths.begin();
@ -256,9 +261,6 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
{
Settings& settings = cppcheck.settings();
if (!loadLibraries(settings))
return EXIT_FAILURE;
if (settings.reportProgress)
mLatestProgressOutputTime = std::time(nullptr);

View File

@ -72,36 +72,38 @@ static std::string stripTemplateParameters(const std::string& funcName) {
void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings)
{
const bool doMarkup = settings->library.markupFile(FileName);
const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase();
// Function declarations..
for (const Scope* scope : symbolDatabase->functionScopes) {
const Function* func = scope->function;
if (!func || !func->token || scope->bodyStart->fileIndex() != 0)
continue;
if (!doMarkup) {
const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase();
for (const Scope* scope : symbolDatabase->functionScopes) {
const Function* func = scope->function;
if (!func || !func->token || scope->bodyStart->fileIndex() != 0)
continue;
// Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor))
if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator())
continue;
// Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor))
if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator())
continue;
if (func->isExtern())
continue;
if (func->isExtern())
continue;
mFunctionDecl.emplace_back(func);
mFunctionDecl.emplace_back(func);
FunctionUsage &usage = mFunctions[stripTemplateParameters(func->name())];
FunctionUsage &usage = mFunctions[stripTemplateParameters(func->name())];
if (!usage.lineNumber)
usage.lineNumber = func->token->linenr();
if (!usage.lineNumber)
usage.lineNumber = func->token->linenr();
// No filename set yet..
if (usage.filename.empty()) {
usage.filename = tokenizer.list.getSourceFilePath();
}
// Multiple files => filename = "+"
else if (usage.filename != tokenizer.list.getSourceFilePath()) {
//func.filename = "+";
usage.usedOtherFile |= usage.usedSameFile;
// No filename set yet..
if (usage.filename.empty()) {
usage.filename = tokenizer.list.getSourceFilePath();
}
// Multiple files => filename = "+"
else if (usage.filename != tokenizer.list.getSourceFilePath()) {
//func.filename = "+";
usage.usedOtherFile |= usage.usedSameFile;
}
}
}
@ -211,7 +213,9 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char Fi
const Token *funcname = nullptr;
if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% (")) {
if (doMarkup)
funcname = Token::Match(tok, "%name% (") ? tok : nullptr;
else if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% (")) {
funcname = tok;
} else if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% <") && Token::simpleMatch(tok->linkAt(1), "> (")) {
funcname = tok;

View File

@ -698,6 +698,13 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
return mExitCode;
}
if (mSettings.library.markupFile(filename)) {
Tokenizer tokenizer(&mSettings, this, &preprocessor);
tokenizer.createTokens(std::move(tokens1));
checkUnusedFunctions.getFileInfo(&tokenizer, &mSettings);
return EXIT_SUCCESS;
}
if (!preprocessor.loadFiles(tokens1, files))
return mExitCode;

View File

@ -0,0 +1,2 @@
Downloaded from here:
https://github.com/HamedMasafi/QML-Samples

View File

@ -0,0 +1,11 @@
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQml.Models 2.12
QtObject {
property string title
property string role
property bool fillWidth: false
property int size: -1
}

View File

@ -0,0 +1,25 @@
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQml.Models 2.12
ItemDelegate {
height: 40
width: parent.width
property bool isHeader: false
RowLayout {
property var _d: model
anchors.fill: parent
anchors.leftMargin: 9
Repeater {
model: root.columns
Label {
text: parent.parent.isHeader ? modelData.title : parent._d[modelData.role]
Layout.fillWidth: modelData.fillWidth
Layout.preferredWidth: modelData.size !== '*'
? modelData.size : 20
}
}
}
}

View File

@ -0,0 +1,27 @@
QT += quick
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
samplemodel.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
samplemodel.h

View File

@ -0,0 +1,30 @@
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQml.Models 2.12
Item {
id: root
property alias model: tableView.model
default property list<TableColumn> columns
TableRow {
id: header
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
background: null
isHeader: true
height: 60
}
ListView {
id: tableView
clip: true
anchors.fill: parent
anchors.topMargin: header.height
delegate: TableRow {}
}
}

View File

@ -0,0 +1,25 @@
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include "samplemodel.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<SampleModel>("Test", 1, 0, "SampleModel");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}

View File

@ -0,0 +1,37 @@
import QtQuick 2.12
import QtQuick.Window 2.12
import Test 1.0
import '.' as Here
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Component.onCompleted: sampleModel.fillSampleData(50)
SampleModel {
id: sampleModel
}
Here.TableView {
anchors.fill: parent
model: sampleModel
TableColumn {
title: qsTr("Id")
role: 'id'
size: 30
}
TableColumn {
title: qsTr("Name")
role: 'name'
fillWidth: true
}
TableColumn {
title: qsTr("Grade")
role: 'grade'
size: 50
}
}
}

View File

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>TableView.qml</file>
<file>TableRow.qml</file>
<file>TableColumn.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1,68 @@
#include "samplemodel.h"
#include <QDebug>
#include <QRandomGenerator>
SampleModel::SampleModel(QObject *parent) : QAbstractListModel(parent)
{
}
int SampleModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return _data.size();
}
QVariant SampleModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() < 0 || index.row() > _data.count() - 1)
return QVariant();
auto row = _data.at(index.row());
switch (role) {
case IdRole:
return index.row();
case NameRole:
return row.first;
case GradeRole:
return row.second;
}
return QVariant();
}
QHash<int, QByteArray> SampleModel::roleNames() const
{
return {
{IdRole, "id"},
{NameRole, "name"},
{GradeRole, "grade"}
};
}
void SampleModel::fillSampleData(int size)
{
QString abs = "qwertyuiopasdfghjklzxcvbnm";
QRandomGenerator r;
for (auto i = 0; i < size; i++) {
Row row;
auto nameLen = r.bounded(3, 8);
QString name;
for (int c = 0; c < nameLen; ++c)
name.append(abs.at(r.bounded(0, abs.size() - 1)));
row.first = name;
row.second = r.bounded(0, 20);
_data.append(row);
}
qDebug() << _data.size() << "item(s) added as sample data";
beginInsertRows(QModelIndex(), 0, _data.size() - 1);
endInsertRows();
}

View File

@ -0,0 +1,30 @@
#ifndef SAMPLEMODEL_H
#define SAMPLEMODEL_H
#include <QAbstractListModel>
class SampleModel : public QAbstractListModel
{
Q_OBJECT
typedef QPair<QString, int> Row;
QList<Row> _data;
public:
enum Role {
IdRole = Qt::UserRole + 1,
NameRole,
GradeRole
};
SampleModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const;
public slots:
void fillSampleData(int size);
};
#endif // SAMPLEMODEL_H

14
test/cli/test-qml.py Normal file
View File

@ -0,0 +1,14 @@
# python3 -m pytest test-qml.py
import os
from testutils import cppcheck
PROJECT_DIR = 'QML-Samples-TableView'
def test_unused_functions():
ret, stdout, stderr = cppcheck(['--library=qt', '--enable=unusedFunction', PROJECT_DIR])
# there are unused functions. But fillSampleData is not unused because that is referenced from main.qml
assert '[unusedFunction]' in stderr
assert 'fillSampleData' not in stderr

View File

@ -189,7 +189,8 @@ private:
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp"
};
check(2, 4, 4,
// the checks are not executed on the markup files => expected result is 2
check(2, 4, 2,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"

View File

@ -193,7 +193,8 @@ private:
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp"
};
check(4, 4,
// checks are not executed on markup files => expected result is 2
check(4, 2,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"

View File

@ -187,7 +187,8 @@ private:
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp"
};
check(2, 4, 4,
// checks are not executed on markup files => expected result is 2
check(2, 4, 2,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"