diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index ebbfc9ca83a1c3192ff882832796a9199dd914a9..a60743d3df6f2b6701afc6b1cd2cb31697f56390 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1395,7 +1395,8 @@ public:
         ItemIsDropEnabled = 8,
         ItemIsUserCheckable = 16,
         ItemIsEnabled = 32,
-        ItemIsTristate = 64
+        ItemIsTristate = 64,
+        ItemNeverHasChildren = 128
     };
     Q_DECLARE_FLAGS(ItemFlags, ItemFlag)
 
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index f1571905914c79c9754286567c8f6726a070113d..b271620ee0a9a86e6b66904adcf44111657570dd 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -2441,6 +2441,7 @@
     \value ItemIsUserCheckable It can be checked or unchecked by the user.
     \value ItemIsEnabled The user can interact with the item.
     \value ItemIsTristate The item is checkable with three separate states.
+    \value ItemNeverHasChildren The item never has child items.
 
     Note that checkable items need to be given both a suitable set of flags
     and an initial state, indicating whether the item is checked or not.
diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp
index ebe38a97cdb955dd44ab101cc4e9286c2a5540b2..ad9be5419bf042871462cc1e7a0608ec28f491d8 100644
--- a/src/corelib/itemmodels/qabstractitemmodel.cpp
+++ b/src/corelib/itemmodels/qabstractitemmodel.cpp
@@ -3271,6 +3271,17 @@ bool QAbstractTableModel::hasChildren(const QModelIndex &parent) const
     return false;
 }
 
+/*!
+    \reimp
+ */
+Qt::ItemFlags QAbstractTableModel::flags(const QModelIndex &index) const
+{
+    Qt::ItemFlags f = QAbstractItemModel::flags(index);
+    if (index.isValid())
+        f |= Qt::ItemNeverHasChildren;
+    return f;
+}
+
 /*!
     \class QAbstractListModel
     \inmodule QtCore
@@ -3391,6 +3402,17 @@ QModelIndex QAbstractListModel::parent(const QModelIndex & /* index */) const
     return QModelIndex();
 }
 
+/*!
+    \reimp
+ */
+Qt::ItemFlags QAbstractListModel::flags(const QModelIndex &index) const
+{
+    Qt::ItemFlags f = QAbstractItemModel::flags(index);
+    if (index.isValid())
+        f |= Qt::ItemNeverHasChildren;
+    return f;
+}
+
 /*!
     \internal
 
diff --git a/src/corelib/itemmodels/qabstractitemmodel.h b/src/corelib/itemmodels/qabstractitemmodel.h
index f138f53487930c8f1538469eb41e8bcc2ccd3a31..8e4f12e9ea3a795bdf2c738fc4dbc43e43556a69 100644
--- a/src/corelib/itemmodels/qabstractitemmodel.h
+++ b/src/corelib/itemmodels/qabstractitemmodel.h
@@ -419,6 +419,7 @@ public:
     bool dropMimeData(const QMimeData *data, Qt::DropAction action,
                       int row, int column, const QModelIndex &parent);
 
+    Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
 protected:
     QAbstractTableModel(QAbstractItemModelPrivate &dd, QObject *parent);
 
@@ -439,6 +440,8 @@ public:
     QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
     bool dropMimeData(const QMimeData *data, Qt::DropAction action,
                       int row, int column, const QModelIndex &parent);
+
+    Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
 protected:
     QAbstractListModel(QAbstractItemModelPrivate &dd, QObject *parent);
 
diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp
index 4d3c7a24ec2be191648083522ef4ab79ca1ae11b..251af273b9bda6119705e08e5c81d9f645a7efc4 100644
--- a/src/widgets/dialogs/qfilesystemmodel.cpp
+++ b/src/widgets/dialogs/qfilesystemmodel.cpp
@@ -964,6 +964,8 @@ Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
         flags |= Qt::ItemIsEditable;
         if (indexNode->isDir())
             flags |= Qt::ItemIsDropEnabled;
+        else
+            flags |= Qt::ItemNeverHasChildren;
     }
     return flags;
 }
diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp
index 1f922dd6e390265dd6bdad90e536c87f03ed47a7..cee47faab4ca951df650da3901731150ded95277 100644
--- a/src/widgets/itemviews/qtreeview.cpp
+++ b/src/widgets/itemviews/qtreeview.cpp
@@ -749,6 +749,8 @@ void QTreeView::expand(const QModelIndex &index)
     Q_D(QTreeView);
     if (!d->isIndexValid(index))
         return;
+    if (index.flags() & Qt::ItemNeverHasChildren)
+        return;
     if (d->delayedPendingLayout) {
         //A complete relayout is going to be performed, just store the expanded index, no need to layout.
         if (d->storeExpanded(index))
@@ -2887,6 +2889,9 @@ void QTreeViewPrivate::expand(int item, bool emitSignal)
 
     if (item == -1 || viewItems.at(item).expanded)
         return;
+    const QModelIndex index = viewItems.at(item).index;
+    if (index.flags() & Qt::ItemNeverHasChildren)
+        return;
 
 #ifndef QT_NO_ANIMATION
     if (emitSignal && animationsEnabled)
@@ -2896,7 +2901,6 @@ void QTreeViewPrivate::expand(int item, bool emitSignal)
     if (state != QAbstractItemView::AnimatingState)
         stateBeforeAnimation = state;
     q->setState(QAbstractItemView::ExpandingState);
-    const QModelIndex index = viewItems.at(item).index;
     storeExpanded(index);
     viewItems[item].expanded = true;
     layout(item);
@@ -3189,7 +3193,7 @@ void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninit
             item->expanded = false;
             item->total = 0;
             item->hasMoreSiblings = false;
-            if (recursiveExpanding || isIndexExpanded(current)) {
+            if ((recursiveExpanding && !(current.flags() & Qt::ItemNeverHasChildren)) || isIndexExpanded(current)) {
                 if (recursiveExpanding)
                     expandedIndexes.insert(current);
                 item->expanded = true;
diff --git a/src/widgets/itemviews/qtreeview_p.h b/src/widgets/itemviews/qtreeview_p.h
index 5a0821c9cf0d255d73d317f697e2b1e0c8fb2dbc..6778446ed3497376b0648172925929adb4e070a7 100644
--- a/src/widgets/itemviews/qtreeview_p.h
+++ b/src/widgets/itemviews/qtreeview_p.h
@@ -203,7 +203,7 @@ public:
 
     inline bool isIndexExpanded(const QModelIndex &idx) const {
         //We first check if the idx is a QPersistentModelIndex, because creating QPersistentModelIndex is slow
-        return isPersistent(idx) && expandedIndexes.contains(idx);
+        return !(idx.flags() & Qt::ItemNeverHasChildren) && isPersistent(idx) && expandedIndexes.contains(idx);
     }
 
     // used when hiding and showing items
diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
index 0f69e951bfbdc561dc02abc5cd1d0be5cb8bf78b..a1de5f190c35ba36f4eac690c81f2a22e706d39a 100644
--- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
+++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
@@ -177,6 +177,7 @@ private slots:
     void emptyModel();
     void removeRows();
     void removeCols();
+    void limitedExpand();
     void expandAndCollapse_data();
     void expandAndCollapse();
     void expandAndCollapseAll();
@@ -1414,6 +1415,45 @@ void tst_QTreeView::removeCols()
     QCOMPARE(view.header()->count(), model.cols);
 }
 
+void tst_QTreeView::limitedExpand()
+{
+    {
+        QStandardItemModel model;
+        QStandardItem *parentItem = model.invisibleRootItem();
+        parentItem->appendRow(new QStandardItem);
+        parentItem->appendRow(new QStandardItem);
+        parentItem->appendRow(new QStandardItem);
+
+        QStandardItem *firstItem = model.item(0, 0);
+        firstItem->setFlags(firstItem->flags() | Qt::ItemNeverHasChildren);
+
+        QTreeView view;
+        view.setModel(&model);
+
+        QSignalSpy spy(&view, SIGNAL(expanded(QModelIndex)));
+        QVERIFY(spy.isValid());
+
+        view.expand(model.index(0, 0));
+        QCOMPARE(spy.count(), 0);
+
+        view.expand(model.index(1, 0));
+        QCOMPARE(spy.count(), 1);
+    }
+    {
+        QStringListModel model(QStringList() << "one" << "two");
+        QTreeView view;
+        view.setModel(&model);
+
+        QSignalSpy spy(&view, SIGNAL(expanded(QModelIndex)));
+        QVERIFY(spy.isValid());
+
+        view.expand(model.index(0, 0));
+        QCOMPARE(spy.count(), 0);
+        view.expandAll();
+        QCOMPARE(spy.count(), 0);
+    }
+}
+
 void tst_QTreeView::expandAndCollapse_data()
 {
     QTest::addColumn<bool>("animationEnabled");