Digraph used in place of a bracket, brace or hash

ID

objectivec.maintainability.avoid_digraphs

Severity

info

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Obscure Syntax

Language

Objective-C

Description

Reports the digraphs <%, %>, <:, :> and %:, which the compiler reads as {, }, [, ] and #. Unlike a trigraph, a digraph is a token spelling rather than a character replacement, so it is not recognised inside a comment or a string literal - occurrences there are text and are not reported. A trigraph is reported by the avoid-trigraphs rule instead.

Rationale

A digraph spells a punctuator with characters that look like something else entirely, so a reader has to translate before they can even see the block structure: if (value > 0) <% opens a brace, and table<:4:> is an array subscript, not a comparison. The two forms of the same punctuator can also be mixed within one file, which defeats brace matching in editors and makes a diff of the affected region unreadable.

The following code illustrates the pattern detected by this rule:

// FLAGGED: Digraph used in place of a bracket, brace or hash
%:define kRetryLimit 3

@implementation Legacy

- (void)arrays {
    // FLAGGED: Digraph used in place of a bracket, brace or hash
    int table<:4:> = {1, 2, 3, 4};
    // FLAGGED: Digraph used in place of a bracket, brace or hash
    NSLog(@"first %d", table<:0:>);
}

- (void)braces:(NSInteger)value {
    // FLAGGED: Digraph used in place of a bracket, brace or hash
    if (value > 0) <% NSLog(@"positive"); %>
}

- (void)plainSyntax:(NSInteger)total count:(NSInteger)count {
    NSArray<NSString *> *names = @[@"a", @"b"];
    NSDictionary *lookup = @{@"key": @1};
    NSInteger rest = total % count;
    NSInteger picked = rest > 0 ? total : count;
    NSLog(@"%@ %@ %ld", names, lookup, (long)picked);
}

- (void)quoted {
    NSString *progress = @"100%> complete";
    // the old build wrote <% here %>
    /* int legacy<:2:>; */
    NSLog(@"%@", progress);
}

@end

Remediation

Replace each digraph with the punctuator it stands for - <% and %> become { and }, <: and :> become [ and ], %: becomes and %:%: becomes #. The digraphs exist only for keyboards that could not produce those characters, which is no longer a constraint worth carrying.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved