Skip to content
June Rhodes edited this page Jun 28, 2024 · 6 revisions

This fork of LLVM/Clang allows you to define static analysis rules in .clang-rules files and have them automatically run during compilation. You do not need to update any other tooling, and it can be used as a drop-in replacement for normal Clang in Unreal Engine builds.

This version of Clang also understands the Unreal Engine UCLASS etc. specifiers so you can match against them. See the AST Matcher Reference page which provides a list of available matchers you can use.

Download and Install

You can download the latest version of Clang for Unreal Engine from GitHub Actions. The latest successfully built version is shown at the top of that page; click through to it and you can download the version of your platform from the "Artifacts" section.

⚠️ IMPORTANT: Do not try to use llvm-win64-msi-18.x yet. The MSI installer doesn't seem to be installing LLVM into the right place. Please use the normal ZIP file distribution as instructed below.

Download the llvm-win64-18.x artifact, which will give you a ZIP file. Extract this ZIP file to C:\Program Files\LLVM such that C:\Program Files\LLVM\bin\clang.exe exists.

To use Clang instead of MSVC in the Unreal build system, open %appdata%\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml (create it if it does not exist), and set the WindowsPlatform/Compiler setting like so:

<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
  <WindowsPlatform>
    <Compiler>Clang</Compiler>
  </WindowsPlatform>
</Configuration>

Getting Started

After installing this fork of Clang, in any directory that you want to have static analysis rules run, create a .clang-rules file. The rules and rulesets you define in this file will apply to all header and source files within and under the directory that the .clang-rules file is in.

When you're creating a .clang-rules file, you'll need to pick a namespace. This should be something unique to your organisation. If you need to reference rules across namespaces in rulesets, use the form namespace/rule.

An example .clang-rules file that we use internally for EOS Online Subsystem looks as follows:

Namespace: redpoint.games
Rules:
  # Disallow 'using namespace' outside of functions.
- Name: using-namespace-in-namespace
  Matcher: |
    namespaceDecl(has(usingDirectiveDecl().bind("using_decl")))
  ErrorMessage: |
    'using namespace' outside of functions pollutes unqualified symbol name resolution
  Callsite: using_decl
  # Detects if a field in a class or struct is not initialized in the 
  # constructor's initialization list when at least one member is initialized
  # via the initializer list.
- Name: field-not-initialized
  Matcher: |
    cxxConstructorDecl(
      unless(isImplicit()),
      unless(isDelegatingConstructor()),
      unless(isDeleted()),
      unless(isDefaulted()),
      hasBody(stmt()),
      unless(ofClass(cxxRecordDecl(isUClass()))),
      unless(ofClass(cxxRecordDecl(isUInterface()))),
      ofClass(cxxRecordDecl(forEach(fieldDecl().bind("declared_field")))),
      forNone(cxxCtorInitializer(forField(fieldDecl(equalsBoundNode("declared_field")).bind("referenced_field"))))
    ).bind("bad_constructor")
  ErrorMessage: |
    one or more fields will be uninitialized when class or struct is constructed; please add the field to the initializer list.
  Callsite: bad_constructor
  Hints:
    declared_field: this field must be initialized
  # Detects when a type must be exported. This is not enabled globally, but rather is enabled
  # via additional .clang-rules files inside 'Public' folders.
- Name: type-must-be-exported
  Matcher: |
    cxxRecordDecl(isMissingDllImportOrExport()).bind('decl')
  ErrorMessage: |
    expected '..._API' to mark this type as exported
  Callsite: decl
  WindowsOnly: true
Rulesets:
  # Ruleset for EOS Online Subsystem plugin.
- Name: eos-online-subsystem
  Severity: Error
  Rules:
  - using-namespace-in-namespace
  - field-not-initialized