Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pkgcheck 0.10.40 (unreleased)
- StabilizationGroupsCheck: check for invalid and non-existant stabilization
groups (Arthur Zamarin)

- GlobalDeclareCheck: detect ``declare -A`` without ``-g`` in global scope
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding that; walking the history to update the news is a minor pain the ass. :)

(Sv. Lockal, #628)


**Packaging:**

Expand Down
48 changes: 48 additions & 0 deletions src/pkgcheck/checks/codingstyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -1667,3 +1667,51 @@ def feed(self, pkg: bash.ParseTree):
if new_index < index:
yield VariableOrderWrong(first_var, self.variable_order[index], pkg=pkg)
index = new_index


class GlobalDeclareWithoutG(results.LineResult, results.Warning):
"""Call to ``declare -A`` without ``-g`` in global scope.

Associative arrays created with ``declare -A`` in global scope
are implicitly local when the ebuild is sourced inside a function
(as non-portage package managers may do).
Use ``declare -gA`` to ensure the variable is always in global scope.
"""

@property
def desc(self):
return f"line {self.lineno}: call to 'declare -A' without '-g' in global scope: {self.line}"


class GlobalDeclareCheck(Check):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In the future, please slot classes. Pkgcheck is lax on this, but the bulk (or base) of pkgcore ecosystem leans towards slotting as a defensive measure against typos in pathways that lack test coverage.

Not a block, just a FYI for future code.

"""Scan ebuilds for ``declare -A`` calls without ``-g`` in global scope."""

_source = sources.EbuildParseRepoSource
known_results = frozenset({GlobalDeclareWithoutG})

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.decl_query = bash.query("(declaration_command) @decl")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I realize you were pointed at "add it to init", but this is effectively a constant- thus should be class level. If you have further refactoring, can you incorporate that?

If not, I'll sort it post merge if I remember.


def feed(self, pkg: bash.ParseTree):
for node in pkg.global_query(self.decl_query):
name_node = node.children[0]
if pkg.node_str(name_node) != "declare":
continue
has_A = False
has_g = False
for child in node.children:
if child.type == "word":
flag = pkg.node_str(child)
if flag.startswith("-"):
if "A" in flag:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

stylistic nit/food for thought: has_A |= "A "in flag

Not a requirement, I'm just pointing it out.

has_A = True
if "g" in flag:
has_g = True
if has_A and not has_g:
lineno, _ = node.start_point
yield GlobalDeclareWithoutG(
line=pkg.node_str(node).split("\n", maxsplit=1)[0].strip(),
lineno=lineno + 1,
pkg=pkg,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"__class__": "GlobalDeclareWithoutG", "category": "GlobalDeclareCheck", "package": "GlobalDeclareWithoutG", "version": "0", "line": "declare -A ASSOC_ARRAY=(", "lineno": 11}
{"__class__": "GlobalDeclareWithoutG", "category": "GlobalDeclareCheck", "package": "GlobalDeclareWithoutG", "version": "0", "line": "declare -rA READONLY_ASSOC=(", "lineno": 16}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
diff -Naur standalone/GlobalDeclareCheck/GlobalDeclareWithoutG/GlobalDeclareWithoutG-0.ebuild fixed/GlobalDeclareCheck/GlobalDeclareWithoutG/GlobalDeclareWithoutG-0.ebuild
--- standalone/GlobalDeclareCheck/GlobalDeclareWithoutG/GlobalDeclareWithoutG-0.ebuild
+++ fixed/GlobalDeclareCheck/GlobalDeclareWithoutG/GlobalDeclareWithoutG-0.ebuild
@@ -5,11 +5,11 @@
LICENSE="BSD"
SLOT="0"

-declare -A ASSOC_ARRAY=(
+declare -gA ASSOC_ARRAY=(
[a]=b
[c]=d
)

-declare -rA READONLY_ASSOC=(
+declare -grA READONLY_ASSOC=(
[e]=f
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

DESCRIPTION="Ebuild with declare without -g in global scope"
HOMEPAGE="https://github.com/pkgcore/pkgcheck"
LICENSE="BSD"
SLOT="0"

declare -A ASSOC_ARRAY=(
[a]=b
[c]=d
)

declare -rA READONLY_ASSOC=(
[e]=f
)

declare -gA GOOD_ASSOC=(
[g]=h
)

declare -g -A ALSO_GOOD=(
[i]=j
)

declare -Ag YET_ANOTHER_GOOD=(
[k]=l
)

declare -rAg GOOD_READONLY=(
[m]=n
)

declare -r READONLY_VAR="foo"

src_prepare() {
declare -A LOCAL_VAR=(
[o]=p
)
}
1 change: 1 addition & 0 deletions testdata/repos/standalone/profiles/categories
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ EclassManualDepsCheck
EclassUsageCheck
EendMissingArgCheck
EqualVersionsCheck
GlobalDeclareCheck
GlobalUseCheck
GlobCheck
HomepageCheck
Expand Down