Add linting tools to GN and fix lint errors. Change-Id: Ife65a275556b0a555a9e3d40d7bae4806a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/23720 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Matt Stark <msta@google.com>
diff --git a/src/gn/starlark/Cargo.toml b/src/gn/starlark/Cargo.toml index f98fdc8..57abfff 100644 --- a/src/gn/starlark/Cargo.toml +++ b/src/gn/starlark/Cargo.toml
@@ -56,7 +56,9 @@ borrow_as_ptr = "warn" cast_lossless = "warn" cloned_instead_of_copied = "warn" -elidable_lifetime_names = "warn" +# Several starlark macros expand to things that break this lint. +# It's a pretty minor thing with no real consequences, so we just allow it. +elidable_lifetime_names = "allow" explicit_into_iter_loop = "warn" explicit_iter_loop = "warn" flat_map_option = "warn"
diff --git a/src/gn/starlark/crates/types/src/errors.rs b/src/gn/starlark/crates/types/src/errors.rs index 24fb1db..e8b2f07 100644 --- a/src/gn/starlark/crates/types/src/errors.rs +++ b/src/gn/starlark/crates/types/src/errors.rs
@@ -26,7 +26,7 @@ impl From<Error> for starlark::Error { fn from(err: Error) -> Self { - starlark::Error::new_other(err) + Self::new_other(err) } }
diff --git a/src/gn/starlark/crates/types/src/file.rs b/src/gn/starlark/crates/types/src/file.rs index a4221e2..81b1dd6 100644 --- a/src/gn/starlark/crates/types/src/file.rs +++ b/src/gn/starlark/crates/types/src/file.rs
@@ -9,7 +9,8 @@ environment::{Methods, MethodsBuilder, MethodsStatic}, starlark_simple_value, values::{ - Freeze, FreezeResult, Freezer, ProvidesStaticType, StarlarkValue, Trace, Tracer, ValueLike, + Freeze, FreezeResult, Freezer, ProvidesStaticType, StarlarkValue, Trace, Tracer, + ValueLike as _, }, }; use starlark_derive::{starlark_module, starlark_value, NoSerialize}; @@ -68,7 +69,7 @@ /// section of a ninja file. pub fn ninja_escaped_path(&self) -> Cow<'static, str> { let s = self.0; - if s.contains(|c| c == ' ' || c == '$' || c == ':') { + if s.contains([' ', '$', ':']) { let mut result = String::with_capacity(s.len()); for c in s.chars() { if c == '$' || c == ' ' || c == ':' { @@ -109,7 +110,7 @@ } impl Freeze for File { - type Frozen = File; + type Frozen = Self; fn freeze(self, _freezer: &Freezer) -> FreezeResult<Self::Frozen> { Ok(self) @@ -127,23 +128,23 @@ } fn write_hash(&self, hasher: &mut StarlarkHasher) -> starlark::Result<()> { - use std::hash::Hash; + use std::hash::Hash as _; self.hash(hasher); Ok(()) } fn equals(&self, other: starlark::values::Value<'v>) -> starlark::Result<bool> { - Ok(other.downcast_ref::<File>().is_some_and(|o| self == o)) + Ok(other.downcast_ref::<Self>().is_some_and(|o| self == o)) } fn collect_repr(&self, collector: &mut String) { - use std::fmt::Write; - write!(collector, "{:?}", self).unwrap(); + use std::fmt::Write as _; + write!(collector, "{self:?}").unwrap(); } fn collect_str(&self, collector: &mut String) { - use std::fmt::Write; - write!(collector, "{}", self).unwrap(); + use std::fmt::Write as _; + write!(collector, "{self}").unwrap(); } } @@ -187,6 +188,14 @@ } } +#[allow(dead_code)] +fn dummy_to_force_cxx_linking() { + // The C++ code depends on the cxx crate being linked for the rust::Str type. + // Because the rust code doesn't depend on the cxx crate, we need a dummy + // function that uses some part of cxx to ensure it links. + drop(cxx::UniquePtr::<cxx::CxxString>::null()); +} + #[cfg(test)] mod tests { use super::*; @@ -236,11 +245,3 @@ a.eq("repr(generated_file)", "'File(\"foo/bar/baz.txt\")'"); } } - -#[allow(dead_code)] -fn dummy_to_force_cxx_linking() { - // The C++ code depends on the cxx crate being linked for the rust::Str type. - // Because the rust code doesn't depend on the cxx crate, we need a dummy - // function that uses some part of cxx to ensure it links. - drop(cxx::UniquePtr::<cxx::CxxString>::null()); -}
diff --git a/src/gn/starlark/crates/types/src/label.rs b/src/gn/starlark/crates/types/src/label.rs index e2fc540..bef5dc7 100644 --- a/src/gn/starlark/crates/types/src/label.rs +++ b/src/gn/starlark/crates/types/src/label.rs
@@ -7,7 +7,8 @@ environment::{Methods, MethodsBuilder, MethodsStatic}, starlark_simple_value, values::{ - Freeze, FreezeResult, Freezer, ProvidesStaticType, StarlarkValue, Trace, Tracer, ValueLike, + Freeze, FreezeResult, Freezer, ProvidesStaticType, StarlarkValue, Trace, Tracer, + ValueLike as _, }, }; use starlark_derive::{starlark_module, starlark_value, NoSerialize}; @@ -60,7 +61,7 @@ let mut iter = s.split(':'); // Verify that there is exactly one colon. match (iter.next(), iter.next(), iter.next()) { - (Some(package), Some(name), None) if !name.is_empty() => Ok(Label { + (Some(package), Some(name), None) if !name.is_empty() => Ok(Self { // Safety: already checked. package: unsafe { PackageRef::new_unchecked(package) }.to_owned(), name: name.to_owned(), @@ -85,7 +86,7 @@ if name.contains(':') { return Err(crate::Error::ColonInRelativeLabel(s.to_owned()).into()); } - Ok(Label { + Ok(Self { package: relative_to.to_owned(), name: name.to_owned(), }) @@ -133,7 +134,7 @@ } impl Freeze for Label { - type Frozen = Label; + type Frozen = Self; fn freeze(self, _freezer: &Freezer) -> FreezeResult<Self::Frozen> { Ok(self) @@ -151,22 +152,22 @@ } fn write_hash(&self, hasher: &mut StarlarkHasher) -> starlark::Result<()> { - use std::hash::Hash; + use std::hash::Hash as _; self.hash(hasher); Ok(()) } fn equals(&self, other: starlark::values::Value<'v>) -> starlark::Result<bool> { - Ok(other.downcast_ref::<Label>().is_some_and(|o| self == o)) + Ok(other.downcast_ref::<Self>().is_some_and(|o| self == o)) } fn collect_repr(&self, collector: &mut String) { - use std::fmt::Write; + use std::fmt::Write as _; write!(collector, "{:?}", self.as_ref()).unwrap(); } fn collect_str(&self, collector: &mut String) { - use std::fmt::Write; + use std::fmt::Write as _; write!(collector, "{}", self.as_ref()).unwrap(); } } @@ -219,7 +220,7 @@ PackageRef::new_for_testing("//foo/bar") ); assert_eq!(lbl.name, "baz"); - assert_eq!(format!("{:?}", lbl), "Label(\"//foo/bar:baz\")"); + assert_eq!(format!("{lbl:?}"), "Label(\"//foo/bar:baz\")"); } #[test]
diff --git a/src/gn/starlark/crates/types/src/label_ref.rs b/src/gn/starlark/crates/types/src/label_ref.rs index b5223c5..e8d10b2 100644 --- a/src/gn/starlark/crates/types/src/label_ref.rs +++ b/src/gn/starlark/crates/types/src/label_ref.rs
@@ -17,6 +17,11 @@ } impl<'a> LabelRef<'a> { + /// Creates a new `LabelRef`. + pub fn new(package: &'a PackageRef, name: &'a str) -> Self { + Self { package, name } + } + /// Returns the package of this label reference. pub fn package(&self) -> &PackageRef { self.package @@ -26,28 +31,21 @@ pub fn name(&self) -> &str { self.name } + + /// Converts this reference into an owned `Label`. + pub fn to_owned(&self) -> Label { + Label::new(self.package.to_owned(), self.name.to_owned()) + } } -impl<'a> std::fmt::Display for LabelRef<'a> { +impl std::fmt::Display for LabelRef<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}:{}", self.package, self.name) } } -impl<'a> std::fmt::Debug for LabelRef<'a> { +impl std::fmt::Debug for LabelRef<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Label(\"{}:{}\")", self.package, self.name) } } - -impl<'a> LabelRef<'a> { - /// Creates a new `LabelRef`. - pub fn new(package: &'a PackageRef, name: &'a str) -> Self { - Self { package, name } - } - - /// Converts this reference into an owned `Label`. - pub fn to_owned(&self) -> Label { - Label::new(self.package.to_owned(), self.name.to_owned()) - } -}
diff --git a/src/gn/starlark/crates/types/src/package_ref.rs b/src/gn/starlark/crates/types/src/package_ref.rs index 0cdfacf..83034d2 100644 --- a/src/gn/starlark/crates/types/src/package_ref.rs +++ b/src/gn/starlark/crates/types/src/package_ref.rs
@@ -26,12 +26,15 @@ /// Creates a new `PackageRef` from a string slice. /// - /// Requires that the string starts with "//" + /// # Safety + /// + /// The caller must ensure that the string `s` is a valid package path + /// starting with "//". pub unsafe fn new_unchecked(s: &str) -> &Self { debug_assert!(s.starts_with("//"), "Package name must start with //"); // Safety: PackageRef is #[repr(transparent)] wrapping str, so their memory // layouts are identical. - unsafe { &*(s as *const str as *const PackageRef) } + unsafe { &*(std::ptr::from_ref(s) as *const Self) } } /// Returns the full package name, eg. "//foo/bar"
diff --git a/src/gn/starlark/crates/types/src/util.rs b/src/gn/starlark/crates/types/src/util.rs index b5729b4..2a6ed80 100644 --- a/src/gn/starlark/crates/types/src/util.rs +++ b/src/gn/starlark/crates/types/src/util.rs
@@ -3,7 +3,13 @@ // found in the LICENSE file. /// Like std::mem::transmute, but can only affect lifetime. -pub unsafe fn extend_lifetime<'to, 'from, T: ?Sized>(val: &'from T) -> &'to T { - // Safety: None - this function is marked unsafe. +/// +/// # Safety +/// +/// The caller must ensure that the returned reference is not used after the +/// underlying data is dropped. +pub unsafe fn extend_lifetime<'to, T: ?Sized>(val: &T) -> &'to T { + // Safety: Transmuting lifetime of reference is unsafe, safety is guaranteed by + // the caller. unsafe { std::mem::transmute(val) } }
diff --git a/tools/run_linter.sh b/tools/run_linter.sh new file mode 100755 index 0000000..5042f27 --- /dev/null +++ b/tools/run_linter.sh
@@ -0,0 +1,13 @@ +#!/bin/bash -eu +# Copyright 2026 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Resolve root directory +cd "$(dirname "$(dirname "$0")")" + +if command -v cargo >/dev/null 2>&1; then + (cd src/gn/starlark && cargo clippy --workspace --all-targets --all-features -- -D warnings) +else + echo "cargo is not installed, skipping Rust linting." +fi