llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
26.71k stars 10.94k forks source link

[LLVM 15.0 Regression] [cplusplus.NewDelete] False positive when using `QPointer<>` #58820

Open kieselnb opened 1 year ago

kieselnb commented 1 year ago

Linting a codebase that uses Qt 6 with clang-tidy 15 (have tested 15.0.0 and 15.0.3) results in a false positive within QWeakPointer (triggered by usage of QPointer):

(py3) [nkiesel@host repro]$ clang-tidy -checks='-*,clang-analyzer-*' -p build/ main.cpp
1 warning generated.
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:162:50: error: Use of memory after it is freed [clang-analyzer-cplusplus.NewDelete,-warnings-as-errors]
        inline void operator delete(void *ptr) { ::operator delete(ptr); }
                                                 ^
/tmp/nkiesel/repro/main.cpp:21:21: note: Calling implicit destructor for 'MainWindow'
    return app.exec();
                    ^
/tmp/nkiesel/repro/main.cpp:21:21: note: Calling implicit destructor for 'QPointer<QWidget>'
/tmp/nkiesel/repro/main.cpp:21:21: note: Calling '~QWeakPointer'
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:579:34: note: Assuming field 'd' is non-null
    inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
                                 ^
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:579:34: note: Left side of '&&' is true
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:579:39: note: Assuming the condition is true
    inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
                                      ^~~~~~~~~~~~~~~~~~~
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:579:30: note: Taking true branch
    inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
                             ^
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:579:60: note: Memory is released
    inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
                                                           ^~~~~~~~
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:579:60: note: Calling 'ExternalRefCountData::operator delete'
    inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
                                                           ^~~~~~~~
/mnt/vol02/nkiesel/opt/qt/Qt-6.2.4/include/QtCore/qsharedpointer_impl.h:162:50: note: Use of memory after it is freed
        inline void operator delete(void *ptr) { ::operator delete(ptr); }
                                                 ^                 ~~~
1 warning treated as error

ExternalRefCountData::operator delete(void*) is incorrectly treated as a proper ::operator delete(void*), and the memory is marked as freed. Parsing then continues down the stack and sees the true call of ::operator delete(void*), triggering the error since clang-tidy thinks the memory has already been freed.

LLVM 12.0.1 and 14.0.6 did not exhibit this behavior.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.11...3.22)

project(repro CXX)

find_package(Qt6 COMPONENTS Gui Widgets)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_executable(repro main.cpp)
target_link_libraries(repro PRIVATE Qt6::Gui Qt6::Widgets)

main.cpp:

#include <QApplication>
#include <QMainWindow>
#include <QWidget>
#include <QPointer>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {}

private:
    QPointer<QWidget> m_widget;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    MainWindow main_window;
    main_window.show();

    return app.exec();
}
llvmbot commented 1 year ago

@llvm/issue-subscribers-clang-static-analyzer

haoNoQ commented 1 year ago

Ohhh interesting. This is why "check in pre-call, model in post-call" is an important rule to follow. I think you're right, if the operator is inlined, we shouldn't model it manually.

rectalogic commented 2 months ago

Still happens with clang-tidy-17 and clang-tidy-18, with Qt 6.7.0

rectalogic commented 2 months ago

Seems related to https://github.com/llvm/llvm-project/issues/62985