Format argument is a variable rather than a literal

ID

objectivec.maintainability.non_literal_format_string

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

String Handling

Language

Objective-C

Description

Reports a call whose format argument is a variable or an expression rather than a string literal - NSLog(message), [NSString stringWithFormat:format], appendFormat:, initWithFormat:, predicateWithFormat:, and the fprintf, sprintf, asprintf, dprintf and syslog families. Use of the <stdio.h> formatted-output functions is reported separately by avoid-stdio.

Rationale

A format string is code, not data. When it is a literal the compiler checks it against the arguments that follow and rejects a mismatch; when it is a variable that check is gone, and every % the value happens to contain becomes a directive consuming an argument that was never passed. The formatter then reads whatever the calling convention left in the argument registers, so %@ prints an arbitrary address as an object and %n writes through one - which is why a message that merely round-trips through this call can crash the process or disclose stack contents. If any part of the value came from outside the program, the caller chooses the directives.

The following code illustrates the pattern detected by this rule:

@implementation Reporter
- (void)reportRaw:(NSString *)message {
    // FLAGGED: Format argument is a variable rather than a literal
    NSLog(message);
}

- (NSString *)currentTemplate { return @"%@"; }
@end

Remediation

Pass the value as an argument to a literal format: NSLog(@"%@", message), [NSString stringWithFormat:@"%@", value], fprintf(stream, "%s", message). Where the template genuinely has to be selected at runtime, take it from a localized strings file with NSLocalizedString so it is still an in-program constant, and never build it from input. For predicateWithFormat: use the %K and %@ substitutions rather than assembling the clause as text.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved