Skip to content

Commit 3e45b40

Browse files
committed
Add position info to path values
(Actually, this adds a position field to *all* values.) This allows improving the "inefficient double copy" warning by showing where the source path came from in the source, e.g. warning: Performing inefficient double copy of path '/home/eelco/Dev/patchelf/' to the store at /home/eelco/Dev/patchelf/flake.nix:30:17. This can typically be avoided by rewriting an attribute like `src = ./.` to `src = builtins.path { path = ./.; name = "source"; }`.
1 parent 8ff43c2 commit 3e45b40

9 files changed

Lines changed: 33 additions & 20 deletions

File tree

src/libexpr/eval.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ PosIdx Value::determinePos(const PosIdx pos) const
149149
// Allow selecting a subset of enum values
150150
#pragma GCC diagnostic push
151151
#pragma GCC diagnostic ignored "-Wswitch-enum"
152+
if (this->pos != 0)
153+
return PosIdx(this->pos);
152154
switch (internalType) {
153155
case tAttrs: return attrs()->pos;
154156
case tLambda: return payload.lambda.fun->pos;
@@ -906,7 +908,7 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
906908

907909
void Value::mkPath(const SourcePath & path)
908910
{
909-
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
911+
mkPath(&*path.accessor, makeImmutableString(path.path.abs()), noPos.get());
910912
}
911913

912914

@@ -2356,7 +2358,7 @@ BackedStringView EvalState::coerceToString(
23562358
// slash, as in /foo/${x}.
23572359
v.payload.path.path
23582360
: copyToStore
2359-
? store->printStorePath(copyPathToStore(context, v.path()))
2361+
? store->printStorePath(copyPathToStore(context, v.path(), v.determinePos(pos)))
23602362
: ({
23612363
auto path = v.path();
23622364
if (path.accessor == rootFS && store->isInStore(path.path.abs())) {
@@ -2434,7 +2436,7 @@ BackedStringView EvalState::coerceToString(
24342436
}
24352437

24362438

2437-
StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePath & path)
2439+
StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePath & path, PosIdx pos)
24382440
{
24392441
if (nix::isDerivation(path.path.abs()))
24402442
error<EvalError>("file names are not allowed to end in '%1%'", drvExtension).debugThrow();
@@ -2448,7 +2450,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
24482450
*store,
24492451
path.resolveSymlinks(SymlinkResolution::Ancestors),
24502452
settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy,
2451-
computeBaseName(path),
2453+
computeBaseName(path, pos),
24522454
ContentAddressMethod::Raw::NixArchive,
24532455
nullptr,
24542456
repair);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ public:
594594
bool coerceMore = false, bool copyToStore = true,
595595
bool canonicalizePath = true);
596596

597-
StorePath copyPathToStore(NixStringContext & context, const SourcePath & path);
597+
StorePath copyPathToStore(NixStringContext & context, const SourcePath & path, PosIdx pos);
598598

599599

600600
/**
@@ -607,7 +607,7 @@ public:
607607
* materialize /nix/store/<hash2>-source though. Still, this
608608
* requires reading/hashing the path twice.
609609
*/
610-
std::string computeBaseName(const SourcePath & path);
610+
std::string computeBaseName(const SourcePath & path, PosIdx pos);
611611

612612
/**
613613
* Path coercion.

src/libexpr/include/nix/expr/nixexpr.hh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ struct ExprPath : Expr
138138
ref<SourceAccessor> accessor;
139139
std::string s;
140140
Value v;
141-
ExprPath(ref<SourceAccessor> accessor, std::string s) : accessor(accessor), s(std::move(s))
141+
ExprPath(ref<SourceAccessor> accessor, std::string s, PosIdx pos) : accessor(accessor), s(std::move(s))
142142
{
143-
v.mkPath(&*accessor, this->s.c_str());
143+
v.mkPath(&*accessor, this->s.c_str(), pos.get());
144144
}
145145
Value * maybeThunk(EvalState & state, Env & env) override;
146146
COMMON_METHODS

src/libexpr/include/nix/expr/value.hh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ struct Value
167167
{
168168
private:
169169
InternalType internalType = tUninitialized;
170+
uint32_t pos{0};
170171

171172
friend std::string showType(const Value & v);
172173

@@ -289,10 +290,11 @@ public:
289290
unreachable();
290291
}
291292

292-
inline void finishValue(InternalType newType, Payload newPayload)
293+
inline void finishValue(InternalType newType, Payload newPayload, uint32_t newPos = 0)
293294
{
294295
payload = newPayload;
295296
internalType = newType;
297+
pos = newPos;
296298
}
297299

298300
/**
@@ -339,9 +341,9 @@ public:
339341
void mkPath(const SourcePath & path);
340342
void mkPath(std::string_view path);
341343

342-
inline void mkPath(SourceAccessor * accessor, const char * path)
344+
inline void mkPath(SourceAccessor * accessor, const char * path, uint32_t pos)
343345
{
344-
finishValue(tPath, { .path = { .accessor = accessor, .path = path } });
346+
finishValue(tPath, { .path = { .accessor = accessor, .path = path } }, pos);
345347
}
346348

347349
inline void mkNull()
@@ -482,6 +484,9 @@ public:
482484

483485
NixFloat fpoint() const
484486
{ return payload.fpoint; }
487+
488+
inline uint32_t getPos() const
489+
{ return pos; }
485490
};
486491

487492

src/libexpr/parser.y

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,8 @@ path_start
374374
root filesystem accessor, rather than the accessor of the
375375
current Nix expression. */
376376
literal.front() == '/'
377-
? new ExprPath(state->rootFS, std::move(path))
378-
: new ExprPath(state->basePath.accessor, std::move(path));
377+
? new ExprPath(state->rootFS, std::move(path), CUR_POS)
378+
: new ExprPath(state->basePath.accessor, std::move(path), CUR_POS);
379379
}
380380
| HPATH {
381381
if (state->settings.pureEval) {
@@ -385,7 +385,7 @@ path_start
385385
);
386386
}
387387
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
388-
$$ = new ExprPath(ref<SourceAccessor>(state->rootFS), std::move(path));
388+
$$ = new ExprPath(ref<SourceAccessor>(state->rootFS), std::move(path), CUR_POS);
389389
}
390390
;
391391

src/libexpr/paths.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,16 @@ std::string EvalState::devirtualize(std::string_view s, const NixStringContext &
5252
return rewriteStrings(std::string(s), rewrites);
5353
}
5454

55-
std::string EvalState::computeBaseName(const SourcePath & path)
55+
std::string EvalState::computeBaseName(const SourcePath & path, PosIdx pos)
5656
{
5757
if (path.accessor == rootFS) {
5858
if (auto storePath = store->maybeParseStorePath(path.path.abs())) {
5959
warn(
60-
"Performing inefficient double copy of path '%s' to the store. "
60+
"Performing inefficient double copy of path '%s' to the store at %s. "
6161
"This can typically be avoided by rewriting an attribute like `src = ./.` "
6262
"to `src = builtins.path { path = ./.; name = \"source\"; }`.",
63-
path);
63+
path,
64+
positions[pos]);
6465
return std::string(fetchToStore(*store, path, FetchMode::DryRun, storePath->name()).to_string());
6566
}
6667
}

src/libexpr/primops.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2620,7 +2620,7 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg
26202620
"while evaluating the second argument (the path to filter) passed to 'builtins.filterSource'");
26212621
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource");
26222622

2623-
addPath(state, pos, state.computeBaseName(path), path, args[0], ContentAddressMethod::Raw::NixArchive, std::nullopt, v, context);
2623+
addPath(state, pos, state.computeBaseName(path, pos), path, args[0], ContentAddressMethod::Raw::NixArchive, std::nullopt, v, context);
26242624
}
26252625

26262626
static RegisterPrimOp primop_filterSource({

src/libexpr/value-to-json.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ json printValueAsJSON(EvalState & state, bool strict,
3939
case nPath:
4040
if (copyToStore)
4141
out = state.store->printStorePath(
42-
state.copyPathToStore(context, v.path()));
42+
state.copyPathToStore(context, v.path(), v.determinePos(pos)));
4343
else
4444
out = v.path().path.abs();
4545
break;

src/libutil/include/nix/util/pos-idx.hh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ class PosIdx
1515
private:
1616
uint32_t id;
1717

18+
public:
1819
explicit PosIdx(uint32_t id)
1920
: id(id)
2021
{
2122
}
2223

23-
public:
2424
PosIdx()
2525
: id(0)
2626
{
@@ -45,6 +45,11 @@ public:
4545
{
4646
return std::hash<uint32_t>{}(id);
4747
}
48+
49+
uint32_t get() const
50+
{
51+
return id;
52+
}
4853
};
4954

5055
inline PosIdx noPos = {};

0 commit comments

Comments
 (0)