Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better pkgs/by-name errors, minor fixes and improvements #290743

Merged
merged 18 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
712ac79
tests.nixpkgs-check-by-name: Improve inherit detection
infinisil Feb 19, 2024
a61c8c9
tests.nixpkgs-check-by-name: Fix allowing non-path overrides
infinisil Feb 19, 2024
24069b9
tests.nixpkgs-check-by-name: Better error for non-syntactic callPackage
infinisil Feb 19, 2024
a53b07e
tests.nixpkgs-check-by-name: Improve error for wrong package file
infinisil Feb 22, 2024
7258d47
tests.nixpkgs-check-by-name: Improve non-syntactic callPackage error …
infinisil Feb 22, 2024
75cdccd
tests.nixpkgs-check-by-name: Improve more errors
infinisil Feb 22, 2024
db7562e
tests.nixpkgs-check-by-name: Improve errors relating to the base branch
infinisil Feb 23, 2024
f5e9b88
tests.nixpkgs-check-by-name: Evaluate base and main branch in parallel
infinisil Feb 23, 2024
eb1f014
tests.nixpkgs-check-by-name: More consistent errors
infinisil Feb 23, 2024
1c4308c
tests.nixpkgs-check-by-name: Better error for empty second arg
infinisil Feb 23, 2024
ce27178
tests.nixpkgs-check-by-name: More spaces in errors
infinisil Feb 23, 2024
d2fa5ba
tests.nixpkgs-check-by-name: Improve errors for new packages
infinisil Feb 23, 2024
64da617
tests.nixpkgs-check-by-name: Fix internal docs
infinisil Feb 23, 2024
2e8d778
tests.nixpkgs-check-by-name: Various minor improvements
infinisil Mar 1, 2024
f056449
tests.nixpkgs-check-by-name: Fix absolute path in errors
infinisil Mar 1, 2024
7858f06
tests.nixpkgs-check-by-name: Better empty argument error
infinisil Mar 1, 2024
5981aff
tests.nixpkgs-check-by-name: Use RelativePath for relative paths
infinisil Mar 1, 2024
fb0a072
tests.nixpkgs-check-by-name: More inline format! arguments
infinisil Mar 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions pkgs/test/nixpkgs-check-by-name/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pkgs/test/nixpkgs-check-by-name/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ lazy_static = "1.4.0"
colored = "2.0.4"
itertools = "0.11.0"
rowan = "0.15.11"
indoc = "2.0.4"
relative-path = "1.9.2"
textwrap = "0.16.1"

[dev-dependencies]
temp-env = "0.3.5"
indoc = "2.0.4"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for myself: this is no longer a dev dependency because it's being used to write some error messages in NixpkgsProblem.

125 changes: 88 additions & 37 deletions pkgs/test/nixpkgs-check-by-name/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ struct Location {
pub column: usize,
}

impl Location {
// Returns the [file] field, but relative to Nixpkgs
fn relative_file(&self, nixpkgs_path: &Path) -> anyhow::Result<PathBuf> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future: This makes me think that Nixpkgs object ought to be passed in here, then use its .path

Ok(self
.file
.strip_prefix(nixpkgs_path)
.with_context(|| {
format!(
"The file ({}) is outside Nixpkgs ({})",
self.file.display(),
nixpkgs_path.display()
)
})?
.to_path_buf())
}
}

#[derive(Deserialize)]
pub enum AttributeVariant {
/// The attribute is not an attribute set, we're limited in the amount of information we can get
Expand Down Expand Up @@ -195,7 +212,6 @@ fn by_name(
use ByNameAttribute::*;

let relative_package_file = structure::relative_file_for_package(attribute_name);
let absolute_package_file = nixpkgs_path.join(&relative_package_file);

// At this point we know that `pkgs/by-name/fo/foo/package.nix` has to exists.
// This match decides whether the attribute `foo` is defined accordingly
Expand Down Expand Up @@ -276,51 +292,86 @@ fn by_name(
// We should expect manual definitions to have a location, otherwise we can't
// enforce the expected format
if let Some(location) = location {
// Parse the Nix file in the location and figure out whether it's an
// attribute definition of the form `= callPackage <arg1> <arg2>`,
// Parse the Nix file in the location
let nix_file = nix_file_store.get(&location.file)?;

// The relative path of the Nix file, for error messages
let relative_file =
location.relative_file(nixpkgs_path).with_context(|| {
format!(
"Failed to resolve location file of attribute {attribute_name}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Failed to resolve location file of attribute {attribute_name}"
"Failed to resolve the file where attribute {attribute_name} is declared"

Not in love with "declared" (possibly: "defined"?) but this sentence doesn't read nicely to me with "location file".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like "defined", will change :)

)
})?;

// Figure out whether it's an attribute definition of the form `= callPackage <arg1> <arg2>`,
// returning the arguments if so.
let optional_syntactic_call_package = nix_file_store
.get(&location.file)?
.call_package_argument_info_at(
let (optional_syntactic_call_package, definition) = nix_file.call_package_argument_info_at(
location.line,
location.column,
// We're passing `pkgs/by-name/fo/foo/package.nix` here, which causes
// the function to verify that `<arg1>` is the same path,
// making `syntactic_call_package.relative_path` end up as `""`
// TODO: This is confusing and should be improved
&absolute_package_file,
)?;
nixpkgs_path,
).with_context(|| format!("Failed to get the definition info for attribute {attribute_name}"))?;

// At this point, we completed two different checks for whether it's a
// `callPackage`
match (is_semantic_call_package, optional_syntactic_call_package) {
// Something like `<attr> = { ... }`
// or a `pkgs.callPackage` but with the wrong file
(false, None)
// Something like `<attr> = pythonPackages.callPackage ./pkgs/by-name/...`
| (false, Some(_))
// Something like `<attr> = bar` where `bar = pkgs.callPackage ...`
// or a `callPackage` but with the wrong file
| (true, None) => {
// All of these are not of the expected form, so error out
// TODO: Make error more specific, don't lump everything together
NixpkgsProblem::WrongCallPackage {
relative_package_file: relative_package_file.to_owned(),
package_name: attribute_name.to_owned(),
}.into()
// Something like `<attr> = foo`
(_, None) => NixpkgsProblem::NonSyntacticCallPackage {
package_name: attribute_name.to_owned(),
file: relative_file,
line: location.line,
column: location.column,
definition,
}
.into(),
// Something like `<attr> = pythonPackages.callPackage ...`
(false, Some(_)) => NixpkgsProblem::NonToplevelCallPackage {
package_name: attribute_name.to_owned(),
file: relative_file,
line: location.line,
column: location.column,
definition,
}
// Something like `<attr> = pkgs.callPackage ./pkgs/by-name/...`,
// with the correct file
.into(),
// Something like `<attr> = pkgs.callPackage ...`
(true, Some(syntactic_call_package)) => {
Success(
// Manual definitions with empty arguments are not allowed
// anymore
if syntactic_call_package.empty_arg {
Loose(())
if let Some(path) = syntactic_call_package.relative_path {
if path == relative_package_file {
// Manual definitions with empty arguments are not allowed
// anymore
Success(if syntactic_call_package.empty_arg {
Loose(NixpkgsProblem::EmptyArgument {
package_name: attribute_name.to_owned(),
file: relative_file,
line: location.line,
column: location.column,
definition,
})
} else {
Tight
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case isn't clear to me (and the indentation is starting to get out of control 😬 )

Might be helped by

  1. Changing the sense of path == relative_package_file to path != relative_package_file and commenting the Tight bound as to why it's Tight.
  2. Putting the if syntactic_call_package.empty_arg check into the else case so it goes down an indentation level.
  3. Finally putting the remaining case into its own block and commenting as to why it's Tight.

} else {
Tight
// Wrong path
NixpkgsProblem::WrongCallPackagePath {
package_name: attribute_name.to_owned(),
file: relative_file,
line: location.line,
column: location.column,
actual_path: path,
expected_path: relative_package_file,
}
.into()
}
)
} else {
// No path
NixpkgsProblem::NonPath {
package_name: attribute_name.to_owned(),
file: relative_file,
line: location.line,
column: location.column,
definition,
}
.into()
}
}
}
} else {
Expand Down Expand Up @@ -408,7 +459,7 @@ fn handle_non_by_name_attribute(
// Parse the Nix file in the location and figure out whether it's an
// attribute definition of the form `= callPackage <arg1> <arg2>`,
// returning the arguments if so.
let optional_syntactic_call_package = nix_file_store
let (optional_syntactic_call_package, _definition) = nix_file_store
.get(&location.file)?
.call_package_argument_info_at(
location.line,
Expand Down Expand Up @@ -453,7 +504,7 @@ fn handle_non_by_name_attribute(
_ => {
// Otherwise, the path is outside `pkgs/by-name`, which means it can be
// migrated
Loose(syntactic_call_package)
Loose((syntactic_call_package, location.file))
}
}
}
Expand Down
Loading
Loading