Skip to content

Commit

Permalink
Add handling for catching severe errors via libclang
Browse files Browse the repository at this point in the history
  • Loading branch information
mannprerak2 committed Nov 18, 2023
1 parent 5dca10e commit 835c436
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 8 deletions.
5 changes: 5 additions & 0 deletions pkgs/ffigen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 10.1.0

- Add flag and config `ignore-source-errors`. This prevents bindings to be generated
until source errors are resolved or manually ignore via this flag.

## 10.0.0

- Stable release targeting Dart 3.2 using new `dart:ffi` features available
Expand Down
18 changes: 18 additions & 0 deletions pkgs/ffigen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,24 @@ use-supported-typedefs: true
```yaml
use-dart-handle: true
```

</td>
</tr>
<tr>
<td>ignore-source-errors</td>
<td>Where to ignore compiler warnings/errors in source header files.<br>
<b>Default: false</b>
</td>
<td>

```yaml
ignore-source-errors: true
```
and/or via the command line -
```bash
dart run ffigen --ignore-source-errors
```

</td>
</tr>
<tr>
Expand Down
14 changes: 14 additions & 0 deletions pkgs/ffigen/lib/src/config_provider/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ class Config {
FfiNativeConfig get ffiNativeConfig => _ffiNativeConfig;
late FfiNativeConfig _ffiNativeConfig;

/// Where to ignore compiler warnings/errors in source header files.
bool get ignoreSourceErrors => _ignoreSourceErrors;
set ignoreSourceErrors(bool n) {
_ignoreSourceErrors = _ignoreSourceErrors || n;
}

bool _ignoreSourceErrors = false;

Config._({required this.filename, required this.packageConfig});

/// Create config from Yaml map.
Expand Down Expand Up @@ -285,6 +293,12 @@ class Config {
transform: (node) => headersExtractor(node.value, filename),
result: (node) => _headers = node.value,
)),
HeterogeneousMapEntry(
key: strings.ignoreSourceErrors,
valueConfigSpec: BoolConfigSpec(),
defaultValue: (node) => false,
resultOrDefault: (node) => ignoreSourceErrors = node.value as bool,
),
HeterogeneousMapEntry(
key: strings.compilerOpts,
valueConfigSpec: OneOfConfigSpec<List<String>, List<String>>(
Expand Down
10 changes: 10 additions & 0 deletions pkgs/ffigen/lib/src/executables/ffigen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final _logger = Logger('ffigen.ffigen');
final _ansi = Ansi(Ansi.terminalSupportsAnsi);

const compilerOpts = 'compiler-opts';
const ignoreSourceErrors = 'ignore-source-errors';
const conf = 'config';
const help = 'help';
const verbose = 'verbose';
Expand Down Expand Up @@ -87,6 +88,10 @@ Config getConfig(ArgResults result, PackageConfig? packageConfig) {
highPriority: true);
}

if (result.wasParsed(ignoreSourceErrors)) {
config.ignoreSourceErrors = true;
}

return config;
}

Expand Down Expand Up @@ -158,6 +163,11 @@ ArgResults getArgResults(List<String> args) {
compilerOpts,
help: 'Compiler options for clang. (E.g --$compilerOpts "-I/headers -W")',
);
parser.addFlag(
ignoreSourceErrors,
help: 'Ignore any compiler warnings/errors in source header files',
negatable: false,
);

ArgResults results;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,21 @@ class Clang {
late final _clang_formatDiagnostic = _clang_formatDiagnosticPtr
.asFunction<CXString Function(CXDiagnostic, int)>();

/// Determine the severity of the given diagnostic.
int clang_getDiagnosticSeverity(
CXDiagnostic arg0,
) {
return _clang_getDiagnosticSeverity(
arg0,
);
}

late final _clang_getDiagnosticSeverityPtr =
_lookup<ffi.NativeFunction<ffi.Int32 Function(CXDiagnostic)>>(
'clang_getDiagnosticSeverity');
late final _clang_getDiagnosticSeverity =
_clang_getDiagnosticSeverityPtr.asFunction<int Function(CXDiagnostic)>();

/// Same as \c clang_parseTranslationUnit2, but returns
/// the \c CXTranslationUnit instead of an error code. In case of an error this
/// routine returns a \c NULL \c CXTranslationUnit, without further detailed
Expand Down Expand Up @@ -1484,6 +1499,29 @@ abstract class CXDiagnosticDisplayOptions {
static const int CXDiagnostic_DisplayCategoryName = 32;
}

/// Describes the severity of a particular diagnostic.
abstract class CXDiagnosticSeverity {
/// A diagnostic that has been suppressed, e.g., by a command-line
/// option.
static const int CXDiagnostic_Ignored = 0;

/// This diagnostic is a note that should be attached to the
/// previous (non-note) diagnostic.
static const int CXDiagnostic_Note = 1;

/// This diagnostic indicates suspicious code that may not be
/// wrong.
static const int CXDiagnostic_Warning = 2;

/// This diagnostic indicates that the code is ill-formed.
static const int CXDiagnostic_Error = 3;

/// This diagnostic indicates that the code is ill-formed such
/// that future parser recovery is unlikely to produce useful
/// results.
static const int CXDiagnostic_Fatal = 4;
}

/// Flags that control the creation of translation units.
///
/// The enumerators in this enumeration type are meant to be bitwise
Expand Down Expand Up @@ -2641,8 +2679,10 @@ abstract class CXChildVisitResult {
/// The visitor should return one of the \c CXChildVisitResult values
/// to direct clang_visitCursorChildren().
typedef CXCursorVisitor
= ffi.Pointer<ffi.NativeFunction<CXCursorVisitor_function>>;
typedef CXCursorVisitor_function = ffi.Int32 Function(
= ffi.Pointer<ffi.NativeFunction<CXCursorVisitorFunction>>;
typedef CXCursorVisitorFunction = ffi.Int32 Function(
CXCursor cursor, CXCursor parent, CXClientData client_data);
typedef DartCXCursorVisitorFunction = int Function(
CXCursor cursor, CXCursor parent, CXClientData client_data);

/// Opaque pointer representing client data that will be passed through
Expand Down
18 changes: 15 additions & 3 deletions pkgs/ffigen/lib/src/header_parser/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:ffi';
import 'dart:io';
import 'dart:math' show max;

import 'package:ffi/ffi.dart';
import 'package:ffigen/src/code_generator.dart';
Expand All @@ -22,7 +23,7 @@ import 'utils.dart';
Library parse(Config c) {
initParser(c);

final bindings = parseToBindings();
final bindings = parseToBindings(c);

final library = Library(
bindings: bindings,
Expand Down Expand Up @@ -52,7 +53,7 @@ void initParser(Config c) {
}

/// Parses source files and adds generated bindings to [bindings].
List<Binding> parseToBindings() {
List<Binding> parseToBindings(Config c) {
final index = clang.clang_createIndex(0, 0);

Pointer<Pointer<Utf8>> clangCmdArgs = nullptr;
Expand Down Expand Up @@ -84,6 +85,8 @@ List<Binding> parseToBindings() {

final tuList = <Pointer<clang_types.CXTranslationUnitImpl>>[];

var highestDiagnosticLevel =
clang_types.CXDiagnosticSeverity.CXDiagnostic_Ignored;
// Parse all translation units from entry points.
for (final headerLocation in config.headers.entryPoints) {
_logger.fine('Creating TranslationUnit for header: $headerLocation');
Expand All @@ -109,10 +112,19 @@ List<Binding> parseToBindings() {
continue;
}

logTuDiagnostics(tu, _logger, headerLocation);
final diagnosticsLevel = logTuDiagnostics(tu, _logger, headerLocation);
highestDiagnosticLevel = max(highestDiagnosticLevel, diagnosticsLevel);
tuList.add(tu);
}

if (!config.ignoreSourceErrors &&
highestDiagnosticLevel >=
clang_types.CXDiagnosticSeverity.CXDiagnostic_Warning) {
_logger.severe(
"Source headers contains errors. Either resolve them or set flag --ignore-source-errors to generate the bindings.");
exit(1);
}

final tuCursors =
tuList.map((tu) => clang.clang_getTranslationUnitCursor(tu));

Expand Down
11 changes: 9 additions & 2 deletions pkgs/ffigen/lib/src/header_parser/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';
import 'dart:math' show max;

import 'package:ffi/ffi.dart';
import 'package:ffigen/src/code_generator.dart';
Expand All @@ -29,16 +30,21 @@ void visitChildrenResultChecker(int resultCode) {
}

/// Logs the warnings/errors returned by clang for a translation unit.
void logTuDiagnostics(
///
/// Returns the highest [clang_types.CXDiagnosticSeverity] seen, defaults to
/// [clang_types.CXDiagnosticSeverity.CXDiagnostic_Ignored] if none.
int logTuDiagnostics(
Pointer<clang_types.CXTranslationUnitImpl> tu, Logger logger, String header,
{Level logLevel = Level.SEVERE}) {
var result = clang_types.CXDiagnosticSeverity.CXDiagnostic_Ignored;
final total = clang.clang_getNumDiagnostics(tu);
if (total == 0) {
return;
return result;
}
logger.log(logLevel, 'Header $header: Total errors/warnings: $total.');
for (var i = 0; i < total; i++) {
final diag = clang.clang_getDiagnostic(tu, i);
result = max(result, clang.clang_getDiagnosticSeverity(diag));
final cxstring = clang.clang_formatDiagnostic(
diag,
clang_types
Expand All @@ -50,6 +56,7 @@ void logTuDiagnostics(
logger.log(logLevel, ' ${cxstring.toStringAndDispose()}');
clang.clang_disposeDiagnostic(diag);
}
return result;
}

extension CXSourceRangeExt on Pointer<clang_types.CXSourceRange> {
Expand Down
1 change: 1 addition & 0 deletions pkgs/ffigen/lib/src/strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ const supportedNativeType_mappings = <String, SupportedNativeType>{
const sort = 'sort';
const useSupportedTypedefs = 'use-supported-typedefs';
const useDartHandle = 'use-dart-handle';
const ignoreSourceErrors = 'ignore-source-errors';

const comments = 'comments';
// Sub-fields of comments.
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# BSD-style license that can be found in the LICENSE file.

name: ffigen
version: 10.0.0
version: 10.1.0
description: >
Generator for FFI bindings, using LibClang to parse C, Objective-C, and Swift
files.
Expand Down
1 change: 1 addition & 0 deletions pkgs/ffigen/tool/libclang_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ functions:
- clang_disposeIndex
- clang_getNumDiagnostics
- clang_getDiagnostic
- clang_getDiagnosticSeverity
- clang_disposeDiagnostic
- clang_parseTranslationUnit
- clang_disposeTranslationUnit
Expand Down

0 comments on commit 835c436

Please sign in to comment.