You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When Packwerk::ConstNodeInspector finds a constant in a class or module definition, it uses the lexical nesting to fully-qualify that constant’s name. This is not always correct because a namespace may refer to a constant that has already been defined elsewhere, e.g. in another package.
This bug is a variation on #234: this time it’s a reference to an existing constant elsewhere in the enclosing namespace, rather than to the root namespace, which causes the problem.
To Reproduce
Say we have a pair of components, one and two, which define the modules A::B and A::C respectively:
% tree components
components
├── one
│ ├── a
│ │ └── b.rb
│ └── package.yml
└── two
├── a
│ └── c.rb
└── package.yml
Both package.yml files have enforce_dependencies and enforce_privacy set to true and no dependencies defined, so Packwerk should not allow either component to refer to the other.
In addition to defining A::B, the file one/a/b.rb also defines a nested class A::B::D:
moduleAmoduleBclassDdefinfo'defined inside ::A::B by component one'endendendend
And in addition to defining A::C, the file two/a/c.rb reopens that class and overwrites one of its methods:
moduleAmoduleCclassB::Ddefinfo'defined inside ::A::C by component two'endendendend
It might not be immediately obvious that two is redefining A::B::D#info here, but it is, because the reference to B in class B::D gets resolved to the existing constant A::B, regardless of the definition occurring lexically inside module C. (This is what #234 is about.)
So when these two components are loaded, two refers to (and modifies) the class A::B::D from one:
packwerk check doesn’t detect this violation because it doesn’t realise that class B::D is a reference to A::B::D. It assumes it must refer to A::C::B::D because it appears inside module A; module C; …; end end, but Ruby constant resolution is more complex.
This doesn’t present an immediate problem within Shopify because we don’t use compact constant nesting (class B::D) in class definitions, but Packwerk may give incorrect results on external codebases.
Expected behavior
packwerk check should give an error like this:
Dependency violation: ::A::B::D belongs to 'components/one', but 'components/two' does not specify a dependency on 'components/one'.
Are we missing an abstraction?
Is the code making the reference, and the referenced constant, in the right packages?
Inference details: 'B::D' refers to ::A::B::D which seems to be defined in components/one/a/b.rb.
Version information
Packwerk: v0.1.10
Ruby: v2.6.6p146
The text was updated successfully, but these errors were encountered:
This is a copy-paste of @tomstuart's bug report, originally posted to https://github.com/Shopify/packwerk-old/issues/289
Describe the bug
When
Packwerk::ConstNodeInspector
finds a constant in a class or module definition, it uses the lexical nesting to fully-qualify that constant’s name. This is not always correct because a namespace may refer to a constant that has already been defined elsewhere, e.g. in another package.This bug is a variation on #234: this time it’s a reference to an existing constant elsewhere in the enclosing namespace, rather than to the root namespace, which causes the problem.
To Reproduce
Say we have a pair of components,
one
andtwo
, which define the modulesA::B
andA::C
respectively:Both
package.yml
files haveenforce_dependencies
andenforce_privacy
set totrue
and nodependencies
defined, so Packwerk should not allow either component to refer to the other.In addition to defining
A::B
, the fileone/a/b.rb
also defines a nested classA::B::D
:And in addition to defining
A::C
, the filetwo/a/c.rb
reopens that class and overwrites one of its methods:It might not be immediately obvious that
two
is redefiningA::B::D#info
here, but it is, because the reference toB
inclass B::D
gets resolved to the existing constantA::B
, regardless of the definition occurring lexically insidemodule C
. (This is what #234 is about.)So when these two components are loaded,
two
refers to (and modifies) the classA::B::D
fromone
:packwerk check
doesn’t detect this violation because it doesn’t realise thatclass B::D
is a reference toA::B::D
. It assumes it must refer toA::C::B::D
because it appears insidemodule A; module C; …; end end
, but Ruby constant resolution is more complex.This doesn’t present an immediate problem within Shopify because we don’t use compact constant nesting (
class B::D
) in class definitions, but Packwerk may give incorrect results on external codebases.Expected behavior
packwerk check
should give an error like this:Version information
The text was updated successfully, but these errors were encountered: