Skip to content

Commit 6d46dc9

Browse files
k1genroberth
andcommitted
Add warn-short-path-literals setting
Add a new setting to warn about path literals that don't start with "." or "/". When enabled, expressions like `foo/bar` will emit a warning suggesting to use `./foo/bar` instead. A functional test is included. The setting defaults to false for backward compatibility but could eventually default to true in the future. Closes: NixOS#13374 Co-authored-by: Robert Hensing <[email protected]>
1 parent 47f5e5f commit 6d46dc9

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

src/libexpr/include/nix/expr/eval-settings.hh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,21 @@ struct EvalSettings : Config
327327
328328
This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment.
329329
)"};
330+
331+
Setting<bool> warnShortPathLiterals{
332+
this,
333+
false,
334+
"warn-short-path-literals",
335+
R"(
336+
If set to true, the Nix evaluator will warn when encountering relative path literals
337+
that don't start with `./` or `../`.
338+
339+
For example, with this setting enabled, `foo/bar` would emit a warning
340+
suggesting to use `./foo/bar` instead.
341+
342+
This is useful for improving code readability and making path literals
343+
more explicit.
344+
)"};
330345
};
331346

332347
/**

src/libexpr/parser.y

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,15 @@ string_parts_interpolated
365365
path_start
366366
: PATH {
367367
std::string_view literal({$1.p, $1.l});
368+
369+
/* check for short path literals */
370+
if (state->settings.warnShortPathLiterals && literal.front() != '/' && literal.front() != '.') {
371+
logWarning({
372+
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'. (" ANSI_BOLD "warn-short-path-literals" ANSI_NORMAL " = true)", literal, literal),
373+
.pos = state->positions[CUR_POS]
374+
});
375+
}
376+
368377
Path path(absPath(literal, state->basePath.path.abs()));
369378
/* add back in the trailing '/' to the first segment */
370379
if (literal.size() > 1 && literal.back() == '/')

tests/functional/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ suites = [
112112
'impure-eval.sh',
113113
'pure-eval.sh',
114114
'eval.sh',
115+
'short-path-literals.sh',
115116
'repl.sh',
116117
'binary-cache-build-remote.sh',
117118
'search.sh',
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
3+
source common.sh
4+
5+
clearStoreIfPossible
6+
7+
# Test 1: Without the setting (default), no warnings should be produced
8+
nix eval --expr 'test/subdir' 2>"$TEST_ROOT"/stderr
9+
grepQuietInverse < "$TEST_ROOT/stderr" -E "relative path|path literal" || fail "Should not produce warnings by default"
10+
11+
# Test 2: With the setting enabled, warnings should be produced for short path literals
12+
nix eval --warn-short-path-literals --expr 'test/subdir' 2>"$TEST_ROOT"/stderr
13+
grepQuiet "relative path literal 'test/subdir' should be prefixed with '.' for clarity: './test/subdir'" "$TEST_ROOT/stderr"
14+
15+
# Test 3: Different short path literals should all produce warnings
16+
nix eval --warn-short-path-literals --expr 'foo/bar' 2>"$TEST_ROOT"/stderr
17+
grepQuiet "relative path literal 'foo/bar' should be prefixed with '.' for clarity: './foo/bar'" "$TEST_ROOT/stderr"
18+
19+
nix eval --warn-short-path-literals --expr 'a/b/c/d' 2>"$TEST_ROOT"/stderr
20+
grepQuiet "relative path literal 'a/b/c/d' should be prefixed with '.' for clarity: './a/b/c/d'" "$TEST_ROOT/stderr"
21+
22+
# Test 4: Paths starting with ./ should NOT produce warnings
23+
nix eval --warn-short-path-literals --expr './test/subdir' 2>"$TEST_ROOT"/stderr
24+
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
25+
26+
# Test 5: Paths starting with ../ should NOT produce warnings
27+
nix eval --warn-short-path-literals --expr '../test/subdir' 2>"$TEST_ROOT"/stderr
28+
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
29+
30+
# Test 6: Absolute paths should NOT produce warnings
31+
nix eval --warn-short-path-literals --expr '/absolute/path' 2>"$TEST_ROOT"/stderr
32+
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
33+
34+
# Test 7: Test that the warning is at the correct position
35+
nix eval --warn-short-path-literals --expr 'foo/bar' 2>"$TEST_ROOT"/stderr
36+
grepQuiet "at «string»:1:1:" "$TEST_ROOT/stderr"
37+
38+
# Test 8: Test that evaluation still works correctly despite the warning
39+
result=$(nix eval --warn-short-path-literals --expr 'test/subdir' 2>/dev/null)
40+
expected="$PWD/test/subdir"
41+
[[ "$result" == "$expected" ]] || fail "Evaluation result should be correct despite warning"
42+
43+
# Test 9: Test with nix-instantiate as well
44+
nix-instantiate --warn-short-path-literals --eval -E 'foo/bar' 2>"$TEST_ROOT"/stderr
45+
grepQuiet "relative path literal 'foo/bar' should be prefixed" "$TEST_ROOT/stderr"
46+
47+
# Test 10: Test that the setting can be set via configuration
48+
NIX_CONFIG='warn-short-path-literals = true' nix eval --expr 'test/file' 2>"$TEST_ROOT"/stderr
49+
grepQuiet "relative path literal 'test/file' should be prefixed" "$TEST_ROOT/stderr"
50+
51+
# Test 11: Test that command line flag overrides config
52+
NIX_CONFIG='warn-short-path-literals = true' nix eval --no-warn-short-path-literals --expr 'test/file' 2>"$TEST_ROOT"/stderr
53+
grepQuietInverse "relative path literal" "$TEST_ROOT/stderr"
54+
55+
echo "short-path-literals test passed!"

0 commit comments

Comments
 (0)