nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: kubernetes-sigs/node-feature-discovery
Pull request #1233 (2023-05-31 11:35:21)

[release-0.13] nfd-master: add validation of label names and values
Validate labels before trying to update the node. Makes us fail early nad prevent useless retries in case invalid labels are tried.

(backported from commit 2a3c7e4c9357b5426c1203ba7684e6f966a0d65f)


Patch:
diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go
index 198a7108b..29157d6d7 100644
--- a/pkg/nfd-master/nfd-master.go
+++ b/pkg/nfd-master/nfd-master.go
@@ -39,6 +39,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	k8sQuantity "k8s.io/apimachinery/pkg/api/resource"
 	k8sLabels "k8s.io/apimachinery/pkg/labels"
+	k8svalidation "k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/client-go/kubernetes"
 	restclient "k8s.io/client-go/rest"
 	"k8s.io/klog/v2"
@@ -462,6 +463,12 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResource
 		// Add possibly missing default ns
 		label := addNs(label, nfdv1alpha1.FeatureLabelNs)
 
+		//Validate label name
+		if errs := k8svalidation.IsQualifiedName(label); len(errs) > 0 {
+			klog.Errorf("ignoring label %q, invalid name: %s", label, strings.Join(errs, "; "))
+			continue
+		}
+
 		ns, name := splitNs(label)
 
 		// Check label namespace, filter out if ns is not whitelisted
@@ -481,6 +488,12 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResource
 			klog.Errorf("%s (%s) does not match the whitelist (%s) and will not be published.", name, label, m.config.LabelWhiteList.Regexp.String())
 			continue
 		}
+
+		// Validate the label value
+		if errs := k8svalidation.IsValidLabelValue(value); len(errs) > 0 {
+			klog.Errorf("ignoring label %q, invalid value %q: %s", label, value, strings.Join(errs, "; "))
+			continue
+		}
 		outLabels[label] = value
 	}
 


Test patch:
diff --git a/pkg/nfd-master/nfd-master-internal_test.go b/pkg/nfd-master/nfd-master-internal_test.go
index cf821c8e7..848dc1c00 100644
--- a/pkg/nfd-master/nfd-master-internal_test.go
+++ b/pkg/nfd-master/nfd-master-internal_test.go
@@ -367,8 +367,10 @@ func TestSetLabels(t *testing.T) {
 				"random.denied.ns/feature-3":     "val-3",
 				"kubernetes.io/feature-4":        "val-4",
 				"sub.ns.kubernetes.io/feature-5": "val-5",
-				vendorFeatureLabel:               " val-6",
-				vendorProfileLabel:               " val-7"}
+				vendorFeatureLabel:               "val-6",
+				vendorProfileLabel:               "val-7",
+				"--invalid-name--":               "valid-val",
+				"valid-name":                     "--invalid-val--"}
 			expectedPatches := []apihelper.JsonPatch{
 				apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+nfdv1alpha1.WorkerVersionAnnotation, workerVer),
 				apihelper.NewJsonPatch("add", "/metadata/annotations",
Repository: kubernetes-sigs/node-feature-discovery
Pull request #1784 (2024-07-12 04:40:34)

[release-0.16] fix: take into consideration possibility of having empty line in swap file
This is an automated cherry-pick of #1781

/assign marquiz


Patch:
diff --git a/source/memory/memory.go b/source/memory/memory.go
index 5c4ce9f61..ab9bc02ed 100644
--- a/source/memory/memory.go
+++ b/source/memory/memory.go
@@ -131,7 +131,7 @@ func (s *memorySource) GetFeatures() *nfdv1alpha1.Features {
 // detectSwap detects Swap node information
 func detectSwap() (map[string]string, error) {
 	procBasePath := hostpath.ProcDir.Path("swaps")
-	lines, err := getNumberOfLinesFromFile(procBasePath)
+	lines, err := getNumberOfNonEmptyLinesFromFile(procBasePath)
 	if err != nil {
 		return nil, fmt.Errorf("failed to read swaps file: %w", err)
 	}
@@ -195,12 +195,18 @@ func readNdDeviceInfo(path string) nfdv1alpha1.InstanceFeature {
 	return *nfdv1alpha1.NewInstanceFeature(attrs)
 }
 
-func getNumberOfLinesFromFile(path string) (int, error) {
+func getNumberOfNonEmptyLinesFromFile(path string) (int, error) {
 	data, err := os.ReadFile(path)
 	if err != nil {
 		return 0, err
 	}
-	return len(strings.Split(string(data), "\n")), nil
+	length := 0
+	for _, line := range strings.Split(string(data), "\n") {
+		if strings.TrimSpace(line) != "" {
+			length++
+		}
+	}
+	return length, nil
 }
 
 func init() {


Test patch:
diff --git a/source/memory/memory_test.go b/source/memory/memory_test.go
index c5edc1b36..535decb03 100644
--- a/source/memory/memory_test.go
+++ b/source/memory/memory_test.go
@@ -54,7 +54,7 @@ func TestGetNumberofLinesFromFile(t *testing.T) {
 		},
 	}
 	for _, tc := range tc {
-		actual, err := getNumberOfLinesFromFile(tc.path)
+		actual, err := getNumberOfNonEmptyLinesFromFile(tc.path)
 		if tc.expectErr {
 			assert.NotNil(t, err, "should get an error")
 		}
diff --git a/source/memory/testdata/noswap b/source/memory/testdata/noswap
index 37a6cc05c..613dce24e 100644
--- a/source/memory/testdata/noswap
+++ b/source/memory/testdata/noswap
@@ -1,1 +1,1 @@
-Filename                          Type        Size     Used    Priority
\ No newline at end of file
+Filename                          Type        Size     Used    Priority
diff --git a/source/memory/testdata/swap b/source/memory/testdata/swap
index 6ffbd4070..8eb673444 100644
--- a/source/memory/testdata/swap
+++ b/source/memory/testdata/swap
@@ -1,2 +1,2 @@
 Filename                          Type        Size     Used    Priority
-dummyfile   partition   65555   0       -1
\ No newline at end of file
+dummyfile   partition   65555   0       -1
Repository: Azure/ARO-RP
Pull request #1177 (2020-11-18 12:26:08)

Revert tagging attempt 
This does not work.
https://docs.microsoft.com/en-gb/azure/azure-resource-manager/management/tag-resources in the only way which we cant support at this point :/ 


Patch:
diff --git a/pkg/cluster/deploystorage.go b/pkg/cluster/deploystorage.go
index a6929930d..3f6181cd3 100644
--- a/pkg/cluster/deploystorage.go
+++ b/pkg/cluster/deploystorage.go
@@ -236,7 +236,6 @@ func (m *manager) deployStorageTemplate(ctx context.Context, installConfig *inst
 var extraDenyAssignmentExclusions = map[string][]string{
 	"Microsoft.RedHatOpenShift/RedHatEngineering": {
 		"Microsoft.Network/networkInterfaces/effectiveRouteTable/action",
-		"Microsoft.Resources/subscriptions/resourceGroups/write", // enable resource group tagging
 	},
 }
 


Test patch:
diff --git a/pkg/cluster/deploystorage_test.go b/pkg/cluster/deploystorage_test.go
index 39d2bcd28..837f0754e 100644
--- a/pkg/cluster/deploystorage_test.go
+++ b/pkg/cluster/deploystorage_test.go
@@ -44,7 +44,6 @@ var daTestCases = []struct {
 			"Microsoft.Compute/snapshots/write",
 			"Microsoft.Compute/snapshots/delete",
 			"Microsoft.Network/networkInterfaces/effectiveRouteTable/action",
-			"Microsoft.Resources/subscriptions/resourceGroups/write",
 		},
 	},
 }
Repository: PagerDuty/go-pagerduty
Pull request #493 (2023-08-08 14:59:06)

Fix ResponderRequest unmarshalling of IncidentResponders
This PR fixes an unmarshalling bug that was found while trying to use the `ResponderRequestWithContext` method on the client.

The response that the API returns is subtly different to the one which the client library is expecting. Within the `responder_request_target` object, the client library expects a key called `incident_responders`. However when interacting with the API directly, this key is actually `incidents_responders`. Note the additional `s` which pluralises 'incident'.

## Impact

This meant that whenever you made a responder request via the client library, it would succeed but the response would only be partial. That meant you could not retrieve any of the information about the responders which your request had added to an incident.

## A note on API documentation

Please note that there is an inconsistency in the [PagerDuty API documentation for this endpoint](https://developer.pagerduty.com/api-reference/6850764326ee2-create-a-responder-request-for-an-incident).

- In the written documentation it refers to: `incident_responders`
- In the 'response example', it correctly refers to: `incidents_responders`


ChuckCrawford: Thanks for submitting this @allyjweir. Just following up on something internally and then we will get this merged.

Patch:
diff --git a/incident.go b/incident.go
index 1c7286a..48ac4cb 100644
--- a/incident.go
+++ b/incident.go
@@ -659,7 +659,7 @@ type ResponderRequestResponse struct {
 // ResponderRequestTarget specifies an individual target for the responder request.
 type ResponderRequestTarget struct {
 	APIObject
-	Responders IncidentResponders `json:"incident_responders,omitempty"`
+	Responders []IncidentResponders `json:"incidents_responders,omitempty"`
 }
 
 // ResponderRequestTargetWrapper is a wrapper for a ResponderRequestTarget.


Test patch:
diff --git a/incident_test.go b/incident_test.go
index 3d8759c..5108b34 100644
--- a/incident_test.go
+++ b/incident_test.go
@@ -811,12 +811,16 @@ func TestIncident_ResponderRequest(t *testing.T) {
 			"responder_request_target": {
 				"id": "PJ25ZYX",
 				"type": "user_reference",
-				"incident_responders": {
-					"state": "pending",
-					"user": {
-						"id": "PJ25ZYX"
+				"incidents_responders": [
+					{
+						"state": "pending",
+						"user": {
+							"id": "PJ25ZYX",
+							"type": "user_reference",
+							"summary": "dave"
+						}
 					}
-				}
+				]
 			}
 		}]
 	}
@@ -846,8 +850,17 @@ func TestIncident_ResponderRequest(t *testing.T) {
 	want_target := ResponderRequestTarget{}
 	want_target.ID = "PJ25ZYX"
 	want_target.Type = "user_reference"
-	want_target.Responders.State = "pending"
-	want_target.Responders.User.ID = "PJ25ZYX"
+
+	want_target.Responders = []IncidentResponders{
+		{
+			State: "pending",
+			User: APIObject{
+				ID:      "PJ25ZYX",
+				Type:    "user_reference",
+				Summary: "dave",
+			},
+		},
+	}
 
 	want_target_wrapper := ResponderRequestTargetWrapper{Target: want_target}
 	want_targets := []ResponderRequestTargetWrapper{want_target_wrapper}
Repository: Ralith/hecs
Pull request #107 (2020-10-31 17:34:32)

Tweak duplicate component panic wording


Patch:
diff --git a/src/archetype.rs b/src/archetype.rs
index 50db000..f8c21c0 100644
--- a/src/archetype.rs
+++ b/src/archetype.rs
@@ -48,14 +48,14 @@ impl Archetype {
             core::cmp::Ordering::Less => (),
             #[cfg(debug_assertions)]
             core::cmp::Ordering::Equal => panic!(
-                "attempted to create archetype with duplicate components; \
-                    each type must occur at most once! duplicate component type: {}",
+                "attempted to allocate entity with duplicate {} components; \
+                 each type must occur at most once!",
                 x[0].type_name
             ),
             #[cfg(not(debug_assertions))]
             core::cmp::Ordering::Equal => panic!(
-                "attempted to create archetype with duplicate components; \
-            each type must occur at most once!"
+                "attempted to allocate entity with duplicate components; \
+                 each type must occur at most once!"
             ),
             core::cmp::Ordering::Greater => panic!("type info is unsorted"),
         });


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index c8c615f..b94f1d6 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -246,13 +246,13 @@ fn derived_bundle() {
 #[cfg_attr(
     debug_assertions,
     should_panic(
-        expected = "attempted to create archetype with duplicate components; each type must occur at most once! duplicate component type: i32"
+        expected = "attempted to allocate entity with duplicate i32 components; each type must occur at most once!"
     )
 )]
 #[cfg_attr(
     not(debug_assertions),
     should_panic(
-        expected = r#"attempted to create archetype with duplicate components; each type must occur at most once!"#
+        expected = "attempted to allocate entity with duplicate components; each type must occur at most once!"
     )
 )]
 fn bad_bundle_derive() {
@@ -384,13 +384,13 @@ fn query_one() {
 #[cfg_attr(
     debug_assertions,
     should_panic(
-        expected = r#"attempted to create archetype with duplicate components; each type must occur at most once! duplicate component type: f32"#
+        expected = "attempted to allocate entity with duplicate f32 components; each type must occur at most once!"
     )
 )]
 #[cfg_attr(
     not(debug_assertions),
     should_panic(
-        expected = r#"attempted to create archetype with duplicate components; each type must occur at most once!"#
+        expected = "attempted to allocate entity with duplicate components; each type must occur at most once!"
     )
 )]
 fn duplicate_components_panic() {
Repository: Ralith/hecs
Pull request #123 (2020-12-16 16:56:17)

Adds API surface to EntityBuilder to check and modify the current components of the builder.
This is a pretty basic PR.  I contemplate adding `remove` functionality, but it was a lot more involved than this, and while I can think of use-cases I didn't find it very pressing to implement.  Let me know if you want me to update the PR with that functionality though.

AngelOfSol: Updated with respect to your comments, let me know if theres anything else.

Patch:
diff --git a/src/entity_builder.rs b/src/entity_builder.rs
index f346fbd..ec26144 100644
--- a/src/entity_builder.rs
+++ b/src/entity_builder.rs
@@ -105,6 +105,36 @@ impl EntityBuilder {
         self
     }
 
+    /// Checks to see if the component of type `T` exists
+    pub fn has<T: Component>(&self) -> bool {
+        self.indices.contains_key(&TypeId::of::<T>())
+    }
+
+    /// Borrow the component of type `T`, if it exists
+    pub fn get<T: Component>(&self) -> Option<&T> {
+        let index = self.indices.get(&TypeId::of::<T>())?;
+        let (_, offset) = self.info[*index];
+        unsafe {
+            let storage = self.storage.as_ptr().add(offset).cast::<T>();
+            Some(&*storage)
+        }
+    }
+
+    /// Uniquely borrow the component of type `T`, if it exists
+    pub fn get_mut<T: Component>(&mut self) -> Option<&mut T> {
+        let index = self.indices.get(&TypeId::of::<T>())?;
+        let (_, offset) = self.info[*index];
+        unsafe {
+            let storage = self.storage.as_ptr().add(offset).cast::<T>();
+            Some(&mut *storage)
+        }
+    }
+
+    /// Enumerate the types of the entity builder's components
+    pub fn component_types(&self) -> impl Iterator<Item = TypeId> + '_ {
+        self.info.iter().map(|(info, _)| info.id())
+    }
+
     unsafe fn grow(
         min_size: usize,
         cursor: usize,


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 4d46fdd..eb98541 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -144,6 +144,31 @@ fn build_entity() {
     assert_eq!(*world.get::<i32>(f).unwrap(), 789);
 }
 
+#[test]
+fn access_builder_components() {
+    let mut world = World::new();
+    let mut entity = EntityBuilder::new();
+
+    entity.add("abc");
+    entity.add(123);
+
+    assert!(entity.has::<&str>());
+    assert!(entity.has::<i32>());
+    assert!(!entity.has::<usize>());
+
+    assert_eq!(*entity.get::<&str>().unwrap(), "abc");
+    assert_eq!(*entity.get::<i32>().unwrap(), 123);
+    assert_eq!(entity.get::<usize>(), None);
+
+    *entity.get_mut::<i32>().unwrap() = 456;
+    assert_eq!(*entity.get::<i32>().unwrap(), 456);
+
+    let g = world.spawn(entity.build());
+
+    assert_eq!(*world.get::<&str>(g).unwrap(), "abc");
+    assert_eq!(*world.get::<i32>(g).unwrap(), 456);
+}
+
 #[test]
 fn build_entity_bundle() {
     let mut world = World::new();
Repository: Ralith/hecs
Pull request #218 (2021-12-05 02:48:06)

Remove unnecessary unsafe


Patch:
diff --git a/src/bundle.rs b/src/bundle.rs
index 9eb5158..6a30ab3 100644
--- a/src/bundle.rs
+++ b/src/bundle.rs
@@ -14,6 +14,7 @@ use crate::archetype::TypeInfo;
 use crate::Component;
 
 /// A dynamically typed collection of components
+#[allow(clippy::missing_safety_doc)]
 pub unsafe trait DynamicBundle {
     /// Returns a `TypeId` uniquely identifying the set of components, if known
     #[doc(hidden)]
@@ -36,6 +37,7 @@ pub unsafe trait DynamicBundle {
 }
 
 /// A statically typed collection of components
+#[allow(clippy::missing_safety_doc)]
 pub unsafe trait Bundle: DynamicBundle {
     #[doc(hidden)]
     fn with_static_ids<T>(f: impl FnOnce(&[TypeId]) -> T) -> T;
@@ -57,6 +59,7 @@ pub unsafe trait Bundle: DynamicBundle {
 }
 
 /// A dynamically typed collection of cloneable components
+#[allow(clippy::missing_safety_doc)]
 pub unsafe trait DynamicBundleClone: DynamicBundle {
     /// Allow a callback to move all components out of the bundle
     ///
diff --git a/src/entities.rs b/src/entities.rs
index 092a273..1db05f2 100644
--- a/src/entities.rs
+++ b/src/entities.rs
@@ -505,7 +505,10 @@ pub(crate) struct EntityMeta {
 
 impl EntityMeta {
     const EMPTY: EntityMeta = EntityMeta {
-        generation: unsafe { NonZeroU32::new_unchecked(1) },
+        generation: match NonZeroU32::new(1) {
+            Some(x) => x,
+            None => unreachable!(),
+        },
         location: Location {
             archetype: 0,
             index: u32::max_value(), // dummy value, to be filled in
diff --git a/src/query.rs b/src/query.rs
index 8852410..5236663 100644
--- a/src/query.rs
+++ b/src/query.rs
@@ -28,6 +28,7 @@ pub trait Query {
 pub type QueryItem<'a, Q> = <<Q as Query>::Fetch as Fetch<'a>>::Item;
 
 /// Streaming iterators over contiguous homogeneous ranges of components
+#[allow(clippy::missing_safety_doc)]
 pub unsafe trait Fetch<'a>: Sized {
     /// Type of value to be fetched
     type Item;


Test patch:
diff --git a/tests/derive/wrong_lifetime.stderr b/tests/derive/wrong_lifetime.stderr
index f516545..f462edb 100644
--- a/tests/derive/wrong_lifetime.stderr
+++ b/tests/derive/wrong_lifetime.stderr
@@ -4,7 +4,7 @@ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` d
 3 | #[derive(Query)]
   |          ^^^^^
   |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 3:10...
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
  --> $DIR/wrong_lifetime.rs:3:10
   |
 3 | #[derive(Query)]
Repository: Ralith/hecs
Pull request #352 (2023-11-11 01:27:07)

Introduce View::contains


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1bf6d4..f948b2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ### Added
 - `View` and `PreparedView` may now be iterated over, just like queries
+- `View::contains` and `PreparedView::contains` convenience predicates
 
 ### Fixed
 - `CommandBuffer` now executes operations in the order they are recorded
diff --git a/src/query.rs b/src/query.rs
index 682e346..d46fca1 100644
--- a/src/query.rs
+++ b/src/query.rs
@@ -1387,6 +1387,17 @@ impl<'q, Q: Query> View<'q, Q> {
         unsafe { self.get_unchecked(entity) }
     }
 
+    /// Equivalent to `get(entity).is_some()`, but does not require `Q: QueryShared`
+    pub fn contains(&self, entity: Entity) -> bool {
+        let Some(meta) = self.meta.get(entity.id as usize) else {
+            return false;
+        };
+        if meta.generation != entity.generation {
+            return false;
+        }
+        self.fetch[meta.location.archetype as usize].is_some()
+    }
+
     /// Like `get_mut`, but allows simultaneous access to multiple entities
     ///
     /// # Safety
@@ -1559,6 +1570,17 @@ impl<'q, Q: Query> PreparedView<'q, Q> {
         unsafe { self.get_unchecked(entity) }
     }
 
+    /// Equivalent to `get(entity).is_some()`, but does not require `Q: QueryShared`
+    pub fn contains(&self, entity: Entity) -> bool {
+        let Some(meta) = self.meta.get(entity.id as usize) else {
+            return false;
+        };
+        if meta.generation != entity.generation {
+            return false;
+        }
+        self.fetch[meta.location.archetype as usize].is_some()
+    }
+
     /// Like `get_mut`, but allows simultaneous access to multiple entities
     ///
     /// # Safety


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 0543b35..06ad969 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -253,6 +253,9 @@ fn random_access_via_view_mut() {
     assert_eq!(*s, "abc");
 
     assert!(view.get_mut(f).is_none());
+
+    assert!(view.contains(e));
+    assert!(!view.contains(f));
 }
 
 #[test]
Repository: Ralith/hecs
Pull request #397 (2025-04-04 05:18:20)

Rust 1.86 fixes
Fixes for 2 failures that popped up in CI thanks to the Rust 1.86 release.


Patch:
diff --git a/src/archetype.rs b/src/archetype.rs
index f5a7397..3e30856 100644
--- a/src/archetype.rs
+++ b/src/archetype.rs
@@ -418,7 +418,7 @@ impl Archetype {
 impl Drop for Archetype {
     fn drop(&mut self) {
         self.clear();
-        if self.entities.len() == 0 {
+        if self.entities.is_empty() {
             return;
         }
         for (info, data) in self.types.iter().zip(&*self.data) {


Test patch:
diff --git a/tests/derive/wrong_lifetime.stderr b/tests/derive/wrong_lifetime.stderr
index 6af1f9a..7a83260 100644
--- a/tests/derive/wrong_lifetime.stderr
+++ b/tests/derive/wrong_lifetime.stderr
@@ -1,13 +1,11 @@
 error: lifetime may not live long enough
- --> $DIR/wrong_lifetime.rs:3:10
+ --> $DIR/wrong_lifetime.rs:6:5
   |
 3 | #[derive(Query)]
-  |          ^^^^^
-  |          |
-  |          lifetime `'q` defined here
-  |          type annotation requires that `'q` must outlive `'static`
-  |
-  = note: this error originates in the derive macro `Query` (in Nightly builds, run with -Z macro-backtrace for more info)
+  |          ----- lifetime `'q` defined here
+...
+6 |     bar: &'static mut bool,
+  |     ^^^ this usage requires that `'q` must outlive `'static`
 
 error: lifetime may not live long enough
  --> $DIR/wrong_lifetime.rs:9:10
@@ -16,6 +14,6 @@ error: lifetime may not live long enough
   |          ^^^^^
   |          |
   |          lifetime `'q` defined here
-  |          type annotation requires that `'q` must outlive `'static`
+  |          this usage requires that `'q` must outlive `'static`
   |
   = note: this error originates in the derive macro `Query` (in Nightly builds, run with -Z macro-backtrace for more info)
Repository: martinohmann/hcl-rs
Pull request #41 (2022-07-13 19:53:02)

fix: handle escaped newlines in heredocs


Patch:
diff --git a/src/parser/unescape.rs b/src/parser/unescape.rs
index 643bcd7..91f024a 100644
--- a/src/parser/unescape.rs
+++ b/src/parser/unescape.rs
@@ -20,6 +20,7 @@ pub fn unescape(s: &str) -> Result<String> {
         }
 
         let c = match chars.next() {
+            Some('\n') => continue,
             Some('b') => '\u{0008}',
             Some('f') => '\u{000C}',
             Some('n') => '\n',


Test patch:
diff --git a/src/parser/tests.rs b/src/parser/tests.rs
index 5000506..2ec0d1c 100644
--- a/src/parser/tests.rs
+++ b/src/parser/tests.rs
@@ -423,6 +423,11 @@ fn unescape_strings() {
           object_attr = {
             "key\nwith\nnewlines" = true
           }
+
+          heredoc = <<-EOS
+            some string with \
+            escaped newline
+          EOS
         }
     "#;
 
@@ -437,6 +442,7 @@ fn unescape_strings() {
                     "object_attr",
                     Expression::from_iter([("key\nwith\nnewlines", true)]),
                 ))
+                .add_attribute(("heredoc", "some string with escaped newline\n"))
                 .build(),
         )
         .build();