/flags/ruby/ruby-c-syntax-check
ruby -c file.rb: check Ruby syntax without running the script
- tool
- Ruby
- command
- ruby
- flag
- -c
- default
- off (the script is executed)
ruby -c file.rb parses the file and reports syntax errors without running it. A valid file prints Syntax OK and exits with status 0; a broken one prints the error with its line and column and exits with status 1. It is a cheap pre-commit or CI check, and it works on any file that contains Ruby code, including Rakefiles, Gemfiles and .gemspec files.
Valid and broken file
The example runs the official CRuby 3.4 build for WebAssembly from the ruby.wasm project, as a regular command-line program with Node.js; ruby below is that interpreter:
curl -sSfLO https://github.com/ruby/ruby.wasm/releases/download/2.8.1/ruby-3.4-wasm32-unknown-wasip1-full.tar.gz
echo "ccda86a375a4fe09849846d3b03a370172a4902a0c571087f48457388a2762c7 ruby-3.4-wasm32-unknown-wasip1-full.tar.gz" | sha256sum -c --quiet
tar xzf ruby-3.4-wasm32-unknown-wasip1-full.tar.gz
cat > ruby.mjs <<'JS'
// Minimal "ruby" command: runs the official CRuby 3.4 WASI build with the given arguments
import fs from "node:fs";
import { WASI } from "node:wasi";
const root = "ruby-3.4-wasm32-unknown-wasip1-full/usr";
const wasi = new WASI({ version: "preview1", args: ["ruby", "--disable-gems", ...process.argv.slice(2)], env: {}, preopens: { "/usr": root, ".": "." }, returnOnExit: true });
const mod = await WebAssembly.compile(fs.readFileSync(`${root}/local/bin/ruby`));
process.exitCode = wasi.start(await WebAssembly.instantiate(mod, wasi.getImportObject()));
JS
ruby() { node --no-warnings ruby.mjs "$@"; }
cat > good.rb <<'EOF'
def total(items)
items.sum { |i| i[:price] }
end
puts total([{ price: 2 }, { price: 3 }])
EOF
cat > bad.rb <<'EOF'
def total(items)
items.sum { |i| i[:price]
end
EOF
ruby -c good.rb; echo "exit: $?"
ruby -c bad.rb; echo "exit: $?"
echo 'puts "only checked"' | ruby -c; echo "exit: $?"
echo 'BEGIN { puts "BEGIN ran" }' | ruby -cOutput
Syntax OK
exit: 0
ruby: bad.rb:3: syntax errors found (SyntaxError)
1 | def total(items)
2 | items.sum { |i| i[:price]
> 3 | end
| ^ expected a block beginning with `{` to end with `}`
| ^~~ unexpected 'end', assuming it is closing the parent method definition
exit: 1
Syntax OK
exit: 0
Syntax OKgood.rb is not executed: nothing is printed except Syntax OK. For bad.rb, Ruby 3.4's Prism parser points at the missing closing brace and shows the offending lines. Code on standard input is checked too, and even a BEGIN block is not run.
What -c catches and what it does not
| Problem | Found by ruby -c? |
|---|---|
| Missing end, unbalanced braces or brackets | yes |
| Unterminated strings, heredocs, regular expressions | yes |
| Invalid tokens, misplaced keywords | yes |
| Undefined method or variable (NoMethodError, NameError) | no, run time only |
| Missing constants or files (NameError, LoadError) | no, run time only |
| Wrong number of arguments | no, run time only |
| Warnings such as unused variables | only with -w as well: ruby -wc file.rb |
Errors like undefined method for nil and uninitialized constant need the code to run, so tests are still required.
Checking a whole project
# every Ruby file below lib/ and app/, stop at the first failure
find lib app -name '*.rb' -print0 | xargs -0 -n1 ruby -c > /dev/null
# with warnings too
ruby -wc lib/my_gem.rbruby -c accepts one script per run, hence -n1. Redirecting standard output hides the Syntax OK lines; errors go to standard error and still appear.
Pitfalls
- The parser changed in Ruby 3.4 (Prism became the default), so the wording and layout of syntax errors differ from Ruby 3.3 and earlier, which printed
syntax error, unexpected end-of-input. Scripts that grep the message should only rely on the exit status. ruby -cchecks against the syntax of the Ruby version you run. Code that uses newer syntax (for exampleitin blocks, Ruby 3.4) can pass on one version and fail on another; check with the version you deploy.- Combine
-wand-cto see parse-time warnings, such as unused variables, as covered in ruby -w.
FAQ
Does ruby -c find undefined methods or missing constants?
No. It only parses the file. Errors such as undefined method for nil or uninitialized constant happen when the code runs, so use tests, RuboCop or a type checker (Sorbet, Steep) for those.
Can ruby -c check ERB templates?
Not directly, because ERB is not Ruby syntax. Compile the template to Ruby first: erb -x -T - template.erb | ruby -c.
Does ruby -c execute BEGIN blocks or require files?
It does not run the program, so BEGIN blocks and require calls inside the file are not executed. Libraries given on the command line with -r are loaded before the check, so avoid -r with -c if they have side effects.