Scanner fails on C++ module

sonar-reproducer.zip (3.5 MB)

Dear team,

I am using Github Actions with SonarQube Cloud.

My scanner command looks like this:

- name: SonarQube Scan
  id: sonarqube-scan
  uses: SonarSource/sonarqube-scan-action@v5.2
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

- name: Print SonarQube Reproducer
  if: ${{ failure() && steps.sonarqube-scan.conclusion == 'failure' }}
  run: |
    Get-ChildItem -Path ${{ github.workspace }}

- name: Upload SonarQube Reproducer
  if: ${{ failure() && steps.sonarqube-scan.conclusion == 'failure' }}
  uses: actions/upload-artifact@v4
  with:
    name: sonar-reproducer
    path: ${{ github.workspace }}\sonar-cfamily-reproducer.tar.xz

- name: SonarQube Quality Gate check
  uses: sonarsource/sonarqube-quality-gate-action@master
  with:
    pollingTimeoutSec: 600
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

The repository uses C++23 and in particular C++ Modules.

You can find the reproducer attached to this post.

Hope you can already provide a workaround fix and that it helps improving the scanner.

Kind regards,
Felix

Hello @Patschkowski,

Sorry for the delay answering, it took me a while to reduce the reproducer down to a small enough snippet.

It seems to be caused by some sort of inconsistent internal state when using MSVC compatibility mode, because there is no crash when we analyze it in regular mode. The snippet is, if you are curious:

// handler.cpp
module;

namespace std {

// From <atomic>
template <class _Ty>
struct atomic {
public:
  constexpr atomic();

  void store(const _Ty _Value) volatile noexcept {
    // Needs definition
  }
};

// From <dequeue>
template <class _Ty>
class deque {
public:
  deque() {
    // Needs definition
  }
};

} // namespace std

export module foo:handler;

class handler {
  std::deque<int> dq;
};
// parse.cpp
export module foo:parse;
import :handler;

handler my_handler;
// logging.cpp
module;

namespace std {

// #include <atomic>
template <class _Ty> struct atomic {
  void store(_Ty) volatile noexcept;
};

namespace system {

class error_category {
public:
  atomic<unsigned> sc_init_;
  void init_stdcat() { sc_init_.store(1); }
};

} // namespace system

} // namespace std

export module foo:logging;
import :parse;

I have created a high priority internal ticket to follow this up.
Thank you very much for the feedback.

Great job! How can I know that the internal solution has been deployed so I can continue using sonar?

We should ping this thread once a release with a fix is done.

However, you may be able to unblock the analysis already.

I spent some time debugging and I believe the issue may be that we are still enabling delayed template parsing even with C++23, and we should not do that starting with C++20.

We do this because clang used to enable by default -fdelayed-template-parsing for Windows targets (and the documentation still states this), but it will raise a warning (and also crash with the snippet I shared) if explicitly set.

You can “force us” to disable it by passing -fno-delayed-template-parsing to your compiler invocations. It should be a no-op for clang, but it will unset our default.

Please, let me know if this helped.

Hi Alejandro, I added the suggested compiler flag but it still ends up in the scanner fail. Here is the reproducer. Hope it gives more insights
sonar-reproducer (1).zip (3.5 MB)

Hello,

It works on my side. I see this on the logs, first thing from CFamily:

12:14:50.851761700 INFO [thread-main] Creating module scanning reproducer: D:\a\tai\tai\libgem\gem-host_base.cppm

So it doesn’t look like it is even trying to analyze. What is your configuration?

Hi Alejandro,

it matches my observation that after the module dependency scanning it fails immediately.
The sonar properties are defined like this:

# Paths are relative to the sonar-project.properties file.
sonar.sources=libgem/,libgpib/,libhsms/,libsecs2/,libtc/,libtlbmetadata/
sonar.exclusions=**/*.test.cpp
sonar.tests=tests/,libgem/,libgpib/,libhsms/,libsecs2/,libtc/,libtlbmetadata/
sonar.test.inclusions=**/*.test.cpp

sonar.verbose=true

sonar.cfamily.enableModules=true
sonar.cfamily.compile-commands=out/x64-Debug-Clang/compile_commands.json
sonar.cfamily.cobertura.reportPaths=out/x64-Debug-MSVC/unit-test-coverage.xml,out/x64-Debug-MSVC/system-test-coverage.xml
sonar.cfamily.cppunit.reportsPath=out/x64-Debug-MSVC/reports/

sonar.sarifReportPaths=out/x64-Debug-Clang/cppcheck.sarif,out/x64-Debug-MSVC/msvc.sarif

and in the pipeline it looks like this

# This step depends on the compile commands database
# created by CMake for the Clang build.
- name: SonarQube Scan
  id: sonarqube-scan
  uses: SonarSource/sonarqube-scan-action@v5.2
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
  with:
    args: >
      -X -Dsonar.cfamily.reproducer="${{ github.workspace }}\libgem\gem-host_base.cppm"

- name: Upload SonarQube Reproducer
  if: ${{ failure() && steps.sonarqube-scan.conclusion == 'failure' }}
  uses: actions/upload-artifact@v4
  with:
    name: sonar-reproducer
    path: ${{ github.workspace }}\sonar-cfamily-reproducer.tar.xz

- name: SonarQube Quality Gate check
  uses: sonarsource/sonarqube-quality-gate-action@master
  with:
    pollingTimeoutSec: 600
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

That -Dsonar.cfamily.reproducer="${{ github.workspace }}\libgem\gem-host_base.cppm" was probably left when you wanted to get the reproducer?

If it is set, it will force the creation of one regardless of there being any crashes.

Can you try dropping this parameter?

Hi Alejandro,

I can confirm that the addition of -fno-delayed-template-parsing resolved the scanner error.