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.

The llvm-* artifacts include Clang, and these are what you want to download. The MSI artifact provides a ZIP file with an .msi installer. This will install LLVM/Clang in the correct location for UnrealBuildTool to find it.

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
Clone this wiki locally