fix(copyright): drop Erlang format directives and sigil-keyed bindings - #1386
Merged
Conversation
Two Provenant-only copyright/holder false-positive classes in erlang/otp at 6146f0df, neither of which ScanCode emits. An `io:format` control sequence proved a value was a template, but the check sat inside the code-marker set that `!has_copyright_year` gates, so a template carrying a literal year in its own prefix — `Copyright Ericsson AB 2020-~p.` — was exempted as a real dated notice. The directive test is now its own predicate and short-circuits ahead of the year exemption, the way the Windows VERSIONINFO markers already do: a year inside a format string is part of the template, not evidence of a notice. `is_pattern_match_binding_line` kept any line whose quoted text mentioned copyright, so Erlang's `~"copyrights" := Copyrights` map key rescued the binding line it appears on. A quoted key name is not an assigned notice: segments are now tested one at a time, and a lone identifier-shaped token does not count. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Maxim Stykow <maxim.stykow@gmail.com>
Greptile SummaryThe PR narrows two copyright false-positive paths in Erlang source.
Confidence Score: 5/5The PR appears safe to merge with no concrete changed-code defect identified. The new filters are narrowly scoped to Erlang format-control sequences and bare quoted identifiers, while the added regressions preserve dated notices, assigned notice strings, escaped quotes, and non-directive tildes.
|
| Filename | Overview |
|---|---|
| src/copyright/detector/phases/postprocess.rs | Splits quoted text into individual segments and excludes bare identifier-shaped keys from the quoted-notice exemption. |
| src/copyright/detector/tests_false_positives.rs | Adds end-to-end regressions for Erlang sigil-keyed map bindings and format-string false positives. |
| src/copyright/refiner/junk.rs | Extracts the Erlang format-control predicate and applies it before the literal-year exemption for copyrights and holders. |
| src/copyright/refiner/tests.rs | Verifies directive-bearing templates are rejected while ordinary dated notices and non-directive tildes remain accepted. |
Reviews (1): Last reviewed commit: "fix(copyright): drop Erlang format direc..." | Re-trigger Greptile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Verifying erlang/otp at
6146f0df— after the four earlier rounds of copyright fixes — still surfaced five Provenant-only copyright/holder false positives across four Erlang files. ScanCode emits none of them. Both classes are Erlang syntax that the existing guards already had a predicate for, and in both cases the predicate was never reached.A format directive standing in for the end year.
lib/stdlib/scripts/update_deprecations:144,lib/stdlib/test/shell_docs_SUITE.erl:223, anderts/emulator/test/send_term_SUITE.erl:334build a notice withio:format, so the source readsCopyright Ericsson AB 2020-~p. All Rights Reserved.andCopyright 2007, Ericsson AB.~n. fix(copyright): drop notice templates, tooling prose, and format strings #1385 already made~ts/~n/~p/~wcount as template markers, but it put that test inside thehas_code_markersset thatis_junk_copyright_code_fragment/is_junk_holder_code_fragmentgate on&& !has_copyright_year(trimmed)— and every one of these values carries a literal year in the template's own prefix (2020,2021,2007). The year exemption fired first and the directive test never ran. This is the same ordering hazard Greptile flagged on fix(copyright): drop notice templates, tooling prose, and format strings #1385 for the placeholder rule, in a second place.The directive test is now its own predicate,
contains_format_control_directive, and it short-circuits ahead of the year exemption in both functions — the waycontains_windows_versioninfo_tokenalready does. A year inside a format string is part of the template rather than evidence of a notice, so it cannot excuse one.contains_regex_or_template_markerdelegates to the new predicate rather than duplicating the regex, so its other (weaker) markers —\d,{{, a trailing$— keep the year exemption they had.A sigil-quoted map key rescuing a binding line.
.github/scripts/ort-scanner.es:220and:398are Erlang map patterns:#{ ~"copyrights" := Copyrights, ~"licenses" := Licenses} = Summary,. fix(copyright): reject authors and holders that source lines disprove #1382'sis_pattern_match_binding_linecovers:=lines, but it exempts any line whose quoted text mentions copyright — the guard that keeps a realnotice := "Copyright (c) 2020 Acme Corp.". Erlang 27's~"..."binary sigil made the map key a quoted string containingcopyrights, so every one of these lines looked like an assigned notice.A quoted key name is not an assigned notice.
has_quoted_copyrightnow tests quoted segments one at a time and ignores a segment that is a lone identifier-shaped token (copyrights,copyright_notice) — a notice always carries punctuation or a party name alongside its marker. Per-segment testing also closes a latent hole in the old whole-line accumulator, where two neighbouring strings could combine into a marker that neither of them contained ("copy" ++ "right").Issues
Scope and exclusions
src/copyright/refiner/junk.rs, andhas_quoted_copyrightinsrc/copyright/detector/phases/postprocess.rs.is_notice_template_lineand the structured-data/edoc value rules are untouched — a sibling PR owns them. No author-detection, license-index, orBENCHMARKS.mdchanges.How to verify
Fetch the four affected files plus the two guard files and scan them; every remaining value should be a real notice.
Before:
Copyright Ericsson AB 2020-~p. All Rights Reserved./ holderEricsson AB 2020-~p(update_deprecations:144), the same shape with2021-~p(shell_docs_SUITE.erl:223),Copyright 2007, Ericsson AB.~n(send_term_SUITE.erl:334), and the two:=lines with holder'licenses Licenses Summary(ort-scanner.es:220,:398). After: each file reports only its own header notice with the holderEricsson AB.The guards are what bounds the blast radius, so they are worth poking at directly. A real dated notice must survive the directive rule (
Copyright Ericsson AB 1996-2026. All Rights Reserved.,Copyright Ericsson AB 2020-2024.), and a tilde that is not a directive must not condemn one (Copyright (c) 2020 ~ Acme Corp.— a~followed by a word character is not a control sequence, so~petein a URL is out of scope).lib/stdlib/src/io_lib.erlandlib/stdlib/src/shell.erlare the counterpart check: real Erlang sources with 8 and 43 tilde sequences and a genuine header, both of which must still reportCopyright Ericsson AB 1996-2026— the rule is per value, so directives elsewhere in the file are irrelevant. For the key-name rule, a quoted notice being assigned on a:=line must survive whether or not it is dated, including on a sigil-keyed line (#{ ~"notice" := "Copyright Acme Corp. All rights reserved." },) and with an escaped quote inside the string.Intentional differences from Python
Follow-up work
ort-scanner.essurvive and are separate mechanisms rather than this change regressing them — both were present before it.Area, Mappings(:399) is a holder whose own start line is the}, Area, Mappings) ->continuation, which carries neither a:=nor a marker, so no line-scoped rule can see it; dropping the copyright it accompanied leaves it orphaned, which is a symptom of the raw-line filter keying onstart_linealone.licenses deduplicate H T Found)(:476) is comment prose (... in their copyrights and licenses) glued onto the following function head. Both belong to their own changes.Expected-output fixture changes
copyright_golden),post_processing_golden(24), andscanner_integration(22) all pass unchanged, as doescargo test --lib copyright(1254 tests). Scoping the directive rule to the format-control regex rather than hoisting all ofcontains_regex_or_template_markerabove the year exemption is what keeps that true: the weaker markers in that predicate (?, a trailing$) would have been much likelier to condemn a real dated notice.🤖 Generated with Claude Code