Repository: openconfig/goyang
Pull request #241 (2023-04-06 09:08:49)

Added modifier sub statement to pattern - rfc7950#section-9.4.5.1
As per https://www.rfc-editor.org/rfc/rfc7950#section-9.4.5.1, pattern type should support 'modifier' sub statement.
I have made the changes to support 'modifier' for pattern.


Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..a2a66d0
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+  - package-ecosystem: "gomod" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"
+  - package-ecosystem: "github-actions" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"
diff --git a/.github/linters/.golangci.yml b/.github/linters/.golangci.yml
new file mode 100644
index 0000000..dca2af2
--- /dev/null
+++ b/.github/linters/.golangci.yml
@@ -0,0 +1,53 @@
+---
+#########################
+#########################
+## Golang Linter rules ##
+#########################
+#########################
+
+# configure golangci-lint
+# see https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
+run:
+  timeout: 10m
+issues:
+  exclude-rules:
+    - path: _test\.go
+      linters:
+        - dupl
+        - gosec
+        - goconst
+  new: true
+linters:
+  enable:
+    - gosec
+    - unconvert
+    - goconst
+    - goimports
+    - gofmt
+    - gocritic
+    - govet
+    - revive
+    - staticcheck
+    - unconvert
+    - unparam
+    - unused
+    - wastedassign
+    - whitespace
+linters-settings:
+  errcheck:
+    # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
+    # default is false: such cases aren't reported by default.
+    check-blank: true
+  govet:
+    # report about shadowed variables
+    check-shadowing: false
+  maligned:
+    # print struct with more effective memory layout or not, false by default
+    suggest-new: true
+  gocritic:
+    disabled-checks:
+      - singleCaseSwitch
+      - appendAssign
+  revive:
+    ignore-generated-header: true
+    severity: warning
diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml
new file mode 100644
index 0000000..e9ec8be
--- /dev/null
+++ b/.github/linters/.yaml-lint.yml
@@ -0,0 +1,59 @@
+---
+###########################################
+# These are the rules used for            #
+# linting all the yaml files in the stack #
+# NOTE:                                   #
+# You can disable line with:              #
+# # yamllint disable-line                 #
+###########################################
+rules:
+  braces:
+    level: warning
+    min-spaces-inside: 0
+    max-spaces-inside: 0
+    min-spaces-inside-empty: 1
+    max-spaces-inside-empty: 5
+  brackets:
+    level: warning
+    min-spaces-inside: 0
+    max-spaces-inside: 0
+    min-spaces-inside-empty: 1
+    max-spaces-inside-empty: 5
+  colons:
+    level: warning
+    max-spaces-before: 0
+    max-spaces-after: 1
+  commas:
+    level: warning
+    max-spaces-before: 0
+    min-spaces-after: 1
+    max-spaces-after: 1
+  comments: disable
+  comments-indentation: disable
+  document-end: disable
+  document-start:
+    level: warning
+    present: true
+  empty-lines:
+    level: warning
+    max: 2
+    max-start: 0
+    max-end: 0
+  hyphens:
+    level: warning
+    max-spaces-after: 1
+  indentation:
+    level: warning
+    spaces: consistent
+    indent-sequences: true
+    check-multi-line-strings: false
+  key-duplicates: enable
+  line-length:
+    level: warning
+    max: 120
+    allow-non-breakable-words: true
+    allow-non-breakable-inline-mappings: true
+  new-line-at-end-of-file: disable
+  new-lines:
+    type: unix
+  trailing-spaces: disable
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 1eed4df..2419208 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -8,66 +8,8 @@ on:
     - cron: "0 0 * * *"
 
 jobs:
-  build:
-    name: Build
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        go: ['1.12', '1.13', '1.14', '1.15', '1.16', '1.x']
-    steps:
+  go:
+    uses: openconfig/common-ci/.github/workflows/go.yml@125b6b58286d116b216e45c33cb859f547965d61
 
-    - name: Set up Go ${{ matrix.go }}
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go }}
-      id: go
-
-    - name: Check out code into the Go module directory
-      uses: actions/checkout@v2
-
-    - name: Get dependencies
-      run: |
-        go get -v -t -d ./...
-
-    - name: Build
-      run: go build -v ./...
-
-    - name: Test
-      run: go test -v -coverprofile=profile.cov ./... 
-    
-    - name: Race Test
-      run: go test -race ./...
-
-    - name: Coveralls
-      if: ${{ matrix.go == '1.14' }}
-      uses: shogo82148/actions-goveralls@v1
-      with:
-        path-to-profile: profile.cov
-
-  static_analysis:
-    name: Static Analysis
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Install Go
-        uses: actions/setup-go@v2
-        with:
-          go-version: '1.x'
-        id: go
-
-      - name: Install required static analysis tools
-        run: |
-          go install honnef.co/go/tools/cmd/staticcheck@latest
-
-      - name: Check out ygot code
-        uses: actions/checkout@v2
-
-      - name: Go vet
-        run: go vet ./...
-
-      - name: Check gofmt
-        run: diff -u <(echo -n) <(gofmt -d -s .)
-
-      - name: Staticcheck
-        run: |
-          staticcheck ./...
+  linter:
+    uses: openconfig/common-ci/.github/workflows/linter.yml@125b6b58286d116b216e45c33cb859f547965d61
diff --git a/go.mod b/go.mod
index aa57fa1..4b09984 100644
--- a/go.mod
+++ b/go.mod
@@ -3,8 +3,8 @@ module github.com/openconfig/goyang
 go 1.14
 
 require (
-	github.com/google/go-cmp v0.4.0
+	github.com/google/go-cmp v0.6.0
 	github.com/kylelemons/godebug v1.1.0
-	github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600
-	github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3
+	github.com/openconfig/gnmi v0.10.0
+	github.com/pborman/getopt v1.1.0
 )
diff --git a/go.sum b/go.sum
index 428d435..2ba6c05 100644
--- a/go.sum
+++ b/go.sum
@@ -1,71 +1,153 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/protobuf v3.11.4+incompatible/go.mod h1:lUQ9D1ePzbH2PrIS7ob/bjm9HXyH5WHB0Akwh7URreM=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600 h1:EO0ADDEFlykRo0dVFbFglXO16iDoD60IxPJyttU49Ko=
-github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A=
+github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
+github.com/openconfig/gnmi v0.10.0 h1:kQEZ/9ek3Vp2Y5IVuV2L/ba8/77TgjdXg505QXvYmg8=
+github.com/openconfig/gnmi v0.10.0/go.mod h1:Y9os75GmSkhHw2wX8sMsxfI7qRGAEcDh8NTa5a8vj6E=
 github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea/go.mod h1:dhXaV0JgHJzdrHi2l+w0fZrwArtXL7jEFoiqLEdmkvU=
+github.com/openconfig/grpctunnel v0.0.0-20220819142823-6f5422b8ca70/go.mod h1:OmTWe7RyZj2CIzIgy4ovEBzCLBJzRvWSZmn7u02U9gU=
 github.com/openconfig/ygot v0.6.0/go.mod h1:o30svNf7O0xK+R35tlx95odkDmZWS9JyWWQSmIhqwAs=
-github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 h1:YtFkrqsMEj7YqpIhRteVxJxCeC3jJBieuLr0d4C4rSA=
-github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
+github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
+github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/protocolbuffers/txtpbfmt v0.0.0-20220608084003-fc78c767cd6a/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210811021853-ddbe55d93216/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/yang/entry.go b/pkg/yang/entry.go
index eb06437..fa50af1 100644
--- a/pkg/yang/entry.go
+++ b/pkg/yang/entry.go
@@ -148,7 +148,31 @@ type RPCEntry struct {
 type ListAttr struct {
 	MinElements uint64 // leaf-list or list MUST have at least min-elements
 	MaxElements uint64 // leaf-list or list has at most max-elements
-	OrderedBy   *Value // order of entries determined by "system" or "user"
+	// OrderedBy is deprecated. Use OrderedByUser instead.
+	OrderedBy *Value
+	// OrderedByUser indicates whether the entries are "ordered-by user".
+	// Otherwise the order is determined by the system.
+	OrderedByUser bool
+}
+
+// parseOrderedBy parses the ordered-by value and classifies the list/leaf-list
+// by whether the `ordered-by user` modifier is active.
+//
+// For more information see
+// https://datatracker.ietf.org/doc/html/rfc7950#section-7.7.7
+func (l *ListAttr) parseOrderedBy(s *Value) error {
+	if s == nil {
+		return nil
+	}
+	l.OrderedBy = s
+	switch s.Name {
+	case "user":
+		l.OrderedByUser = true
+	case "system":
+	default:
+		return fmt.Errorf("%s: ordered-by has invalid argument: %q", Source(s), s.Name)
+	}
+	return nil
 }
 
 // NewDefaultListAttr returns a new ListAttr object with min/max elements being
@@ -535,11 +559,11 @@ func ToEntry(n Node) (e *Entry) {
 		}
 	}
 	ms := RootNode(n).Modules
-	if e := ms.entryCache[n]; e != nil {
+	if e := ms.getEntryCache(n); e != nil {
 		return e
 	}
 	defer func() {
-		ms.entryCache[n] = e
+		ms.setEntryCache(n, e)
 	}()
 
 	// Copy in the extensions from our Node, if any.
@@ -610,7 +634,9 @@ func ToEntry(n Node) (e *Entry) {
 
 		e = ToEntry(leaf)
 		e.ListAttr = NewDefaultListAttr()
-		e.ListAttr.OrderedBy = s.OrderedBy
+		if err := e.ListAttr.parseOrderedBy(s.OrderedBy); err != nil {
+			e.addError(err)
+		}
 		var err error
 		if e.ListAttr.MaxElements, err = semCheckMaxElements(s.MaxElements); err != nil {
 			e.addError(err)
@@ -647,7 +673,9 @@ func ToEntry(n Node) (e *Entry) {
 	switch s := n.(type) {
 	case *List:
 		e.ListAttr = NewDefaultListAttr()
-		e.ListAttr.OrderedBy = s.OrderedBy
+		if err := e.ListAttr.parseOrderedBy(s.OrderedBy); err != nil {
+			e.addError(err)
+		}
 		var err error
 		if e.ListAttr.MaxElements, err = semCheckMaxElements(s.MaxElements); err != nil {
 			e.addError(err)
@@ -1294,11 +1322,17 @@ func (e *Entry) Find(name string) *Entry {
 			e = e.Parent
 		}
 		if prefix, _ := getPrefix(parts[0]); prefix != "" {
-			m := module(FindModuleByPrefix(contextNode, prefix))
-			if m == nil {
+			mod := FindModuleByPrefix(contextNode, prefix)
+			if mod == nil {
 				e.addError(fmt.Errorf("cannot find module giving prefix %q within context entry %q", prefix, e.Path()))
 				return nil
 			}
+			m := module(mod)
+			if m == nil {
+				e.addError(fmt.Errorf("cannot find which module %q belongs to within context entry %q",
+					mod.NName(), e.Path()))
+				return nil
+			}
 			if m != e.Node.(*Module) {
 				e = ToEntry(m)
 			}
@@ -1316,8 +1350,22 @@ func (e *Entry) Find(name string) *Entry {
 			_, part = getPrefix(part)
 			switch part {
 			case "input":
+				if e.RPC.Input == nil {
+					e.RPC.Input = &Entry{
+						Name: "input",
+						Kind: InputEntry,
+						Dir:  make(map[string]*Entry),
+					}
+				}
 				e = e.RPC.Input
 			case "output":
+				if e.RPC.Output == nil {
+					e.RPC.Output = &Entry{
+						Name: "output",
+						Kind: OutputEntry,
+						Dir:  make(map[string]*Entry),
+					}
+				}
 				e = e.RPC.Output
 			}
 		default:
@@ -1433,6 +1481,12 @@ func (e *Entry) dup() *Entry {
 			ne.Dir[k] = de
 		}
 	}
+
+	ne.Extra = make(map[string][]interface{})
+	for k, v := range e.Extra {
+		ne.Extra[k] = v
+	}
+
 	return &ne
 }
 
@@ -1457,6 +1511,9 @@ func (e *Entry) merge(prefix *Value, namespace *Value, oe *Entry) {
 		} else {
 			v.Parent = e
 			v.Exts = append(v.Exts, oe.Exts...)
+			for lk := range oe.Extra {
+				v.Extra[lk] = append(v.Extra[lk], oe.Extra[lk]...)
+			}
 			e.Dir[k] = v
 		}
 	}
diff --git a/pkg/yang/modules.go b/pkg/yang/modules.go
index 1d47a4e..ab543d2 100644
--- a/pkg/yang/modules.go
+++ b/pkg/yang/modules.go
@@ -26,14 +26,16 @@ import (
 // Modules contains information about all the top level modules and
 // submodules that are read into it via its Read method.
 type Modules struct {
-	Modules    map[string]*Module // All "module" nodes
-	SubModules map[string]*Module // All "submodule" nodes
-	includes   map[*Module]bool   // Modules we have already done include on
-	nsMu       sync.Mutex         // nsMu protects the byNS map.
-	byNS       map[string]*Module // Cache of namespace lookup
-	typeDict   *typeDictionary    // Cache for type definitions.
+	Modules      map[string]*Module // All "module" nodes
+	SubModules   map[string]*Module // All "submodule" nodes
+	includes     map[*Module]bool   // Modules we have already done include on
+	nsMu         sync.Mutex         // nsMu protects the byNS map.
+	byNS         map[string]*Module // Cache of namespace lookup
+	typeDict     *typeDictionary    // Cache for type definitions.
+	entryCacheMu sync.RWMutex       // entryCacheMu protects the entryCache map.
 	// entryCache is used to prevent unnecessary recursion into previously
-	// converted nodes.
+	// converted nodes. To access the map, use the get/set/ClearEntryCache()
+	// thread-safe functions.
 	entryCache map[Node]*Entry
 	// mergedSubmodule is used to prevent re-parsing a submodule that has already
 	// been merged into a particular entity when circular dependencies are being
@@ -323,7 +325,7 @@ func (ms *Modules) Process() []error {
 	// Reset globals that may remain stale if multiple Process() calls are
 	// made by the same caller.
 	ms.mergedSubmodule = map[string]bool{}
-	ms.entryCache = map[Node]*Entry{}
+	ms.ClearEntryCache()
 
 	errs := ms.process()
 	if len(errs) > 0 {
@@ -442,3 +444,23 @@ func (ms *Modules) include(m *Module) error {
 	}
 	return nil
 }
+
+func (ms *Modules) getEntryCache(n Node) *Entry {
+	ms.entryCacheMu.RLock()
+	defer ms.entryCacheMu.RUnlock()
+	return ms.entryCache[n]
+}
+
+func (ms *Modules) setEntryCache(n Node, e *Entry) {
+	ms.entryCacheMu.Lock()
+	defer ms.entryCacheMu.Unlock()
+	ms.entryCache[n] = e
+}
+
+// ClearEntryCache clears the entryCache containing previously converted nodes
+// used by the ToEntry function.
+func (ms *Modules) ClearEntryCache() {
+	ms.entryCacheMu.Lock()
+	defer ms.entryCacheMu.Unlock()
+	ms.entryCache = map[Node]*Entry{}
+}
diff --git a/pkg/yang/yang.go b/pkg/yang/yang.go
index 4f62801..efff44a 100644
--- a/pkg/yang/yang.go
+++ b/pkg/yang/yang.go
@@ -1046,6 +1046,7 @@ func (s *Length) Statement() *Statement { return s.Source }
 func (s *Length) Exts() []*Statement    { return s.Extensions }
 
 // A Pattern is defined in: http://tools.ietf.org/html/rfc6020#section-9.4.6
+// and http://tools.ietf.org/html/rfc7950#section-9.4.5.1 ("modifier" sub-statement)
 type Pattern struct {
 	Name       string       `yang:"Name,nomerge"`
 	Source     *Statement   `yang:"Statement,nomerge"`
@@ -1056,6 +1057,7 @@ type Pattern struct {
 	ErrorAppTag  *Value `yang:"error-app-tag"`
 	ErrorMessage *Value `yang:"error-message"`
 	Reference    *Value `yang:"reference"`
+	Modifier     *Value `yang:"modifier"`
 }
 
 func (Pattern) Kind() string             { return "pattern" }
diff --git a/pkg/yangentry/build_yang.go b/pkg/yangentry/build_yang.go
index 2841bdf..8cb0663 100644
--- a/pkg/yangentry/build_yang.go
+++ b/pkg/yangentry/build_yang.go
@@ -36,6 +36,9 @@ func Parse(yangfiles, path []string) (map[string]*yang.Entry, []error) {
 
 	var processErr []error
 	for _, name := range yangfiles {
+		if name == "" {
+			continue
+		}
 		if err := ms.Read(name); err != nil {
 			processErr = append(processErr, err)
 		}


Test patch:
diff --git a/pkg/yang/entry_test.go b/pkg/yang/entry_test.go
index b2a5ae0..2000238 100644
--- a/pkg/yang/entry_test.go
+++ b/pkg/yang/entry_test.go
@@ -2058,6 +2058,7 @@ var testIfFeatureModules = []struct {
   feature ft-identity;
   feature ft-uses;
   feature ft-refine;
+  feature ft-augment-uses;
 
   container cont {
     if-feature ft-container;
@@ -2118,6 +2119,9 @@ var testIfFeatureModules = []struct {
 
   augment "/cont" {
     if-feature ft-augment;
+	uses g {
+		if-feature ft-augment-uses;
+	}
   }
 
   identity id {
@@ -2130,7 +2134,10 @@ var testIfFeatureModules = []struct {
       if-feature ft-refine;
     }
   }
-  grouping g {}
+
+  grouping g {
+	container gc {}
+  }
 }
 `,
 	},
@@ -2269,6 +2276,18 @@ func TestIfFeature(t *testing.T) {
 			inIfFeatures:   ms.Modules["if-feature"].Uses[0].IfFeature,
 			wantIfFeatures: []string{"ft-uses"},
 		},
+		{
+			// Verify that if-feature field defined in "uses" is correctly propagated to container
+			name:           "uses",
+			inIfFeatures:   entryIfFeatures(mod.Dir["gc"]),
+			wantIfFeatures: []string{"ft-uses"},
+		},
+		{
+			// Verify that if-feature field defined in "augment" and in "augment > uses" is correctly propagated to container
+			name:           "augment-uses",
+			inIfFeatures:   entryIfFeatures(mod.Dir["cont"].Dir["gc"]),
+			wantIfFeatures: []string{"ft-augment-uses", "ft-augment"},
+		},
 	}
 
 	for _, tc := range testcases {
@@ -3958,3 +3977,165 @@ func TestLess(t *testing.T) {
 		}
 	}
 }
+
+type customTestCases struct {
+	wantEntryPath       string
+	wantEntryCustomTest func(t *testing.T, e *Entry)
+}
+
+func TestOrderedBy(t *testing.T) {
+	tests := []struct {
+		name          string
+		inModules     map[string]string
+		testcases     []customTestCases
+		wantErrSubstr string
+	}{{
+		name: "ordered-by user",
+		inModules: map[string]string{
+			"test.yang": `
+			module test {
+				prefix "t";
+				namespace "urn:t";
+
+				list ordered-list {
+					key "name";
+					ordered-by user;
+					leaf name {
+						type string;
+					}
+				}
+
+				list unordered-list {
+					key "name";
+					ordered-by system;
+					leaf name {
+						type string;
+					}
+				}
+
+				list unordered-list2 {
+					key "name";
+					leaf name {
+						type string;
+					}
+				}
+
+				leaf-list ordered-leaflist {
+					ordered-by user;
+					type string;
+				}
+
+				leaf-list unordered-leaflist {
+					ordered-by system;
+					type string;
+				}
+
+				leaf-list unordered-leaflist2 {
+					type string;
+				}
+			}
+			`,
+		},
+		testcases: []customTestCases{{
+			wantEntryPath: "/test/ordered-list",
+			wantEntryCustomTest: func(t *testing.T, e *Entry) {
+				if got, want := e.ListAttr.OrderedByUser, true; got != want {
+					t.Errorf("got %v, want %v", got, want)
+				}
+			},
+		}, {
+			wantEntryPath: "/test/unordered-list",
+			wantEntryCustomTest: func(t *testing.T, e *Entry) {
+				if got, want := e.ListAttr.OrderedByUser, false; got != want {
+					t.Errorf("got %v, want %v", got, want)
+				}
+			},
+		}, {
+			wantEntryPath: "/test/unordered-list2",
+			wantEntryCustomTest: func(t *testing.T, e *Entry) {
+				if got, want := e.ListAttr.OrderedByUser, false; got != want {
+					t.Errorf("got %v, want %v", got, want)
+				}
+			},
+		}, {
+			wantEntryPath: "/test/ordered-leaflist",
+			wantEntryCustomTest: func(t *testing.T, e *Entry) {
+				if got, want := e.ListAttr.OrderedByUser, true; got != want {
+					t.Errorf("got %v, want %v", got, want)
+				}
+			},
+		}, {
+			wantEntryPath: "/test/unordered-leaflist",
+			wantEntryCustomTest: func(t *testing.T, e *Entry) {
+				if got, want := e.ListAttr.OrderedByUser, false; got != want {
+					t.Errorf("got %v, want %v", got, want)
+				}
+			},
+		}, {
+			wantEntryPath: "/test/unordered-leaflist2",
+			wantEntryCustomTest: func(t *testing.T, e *Entry) {
+				if got, want := e.ListAttr.OrderedByUser, false; got != want {
+					t.Errorf("got %v, want %v", got, want)
+				}
+			},
+		}},
+	}, {
+		name: "ordered-by client: invalid argument",
+		inModules: map[string]string{
+			"test.yang": `
+			module test {
+				prefix "t";
+				namespace "urn:t";
+
+				list ordered-list {
+					key "name";
+					ordered-by client;
+					leaf name {
+						type string;
+					}
+				}
+			}
+			`,
+		},
+		wantErrSubstr: "ordered-by has invalid argument",
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ms := NewModules()
+			var errs []error
+			for n, m := range tt.inModules {
+				if err := ms.Parse(m, n); err != nil {
+					errs = append(errs, err)
+				}
+			}
+
+			if len(errs) > 0 {
+				t.Fatalf("ms.Parse(), got unexpected error parsing input modules: %v", errs)
+			}
+
+			if errs := ms.Process(); len(errs) > 0 {
+				if len(errs) == 1 {
+					if diff := errdiff.Substring(errs[0], tt.wantErrSubstr); diff != "" {
+						t.Fatalf("did not get expected error, %s", diff)
+					}
+					return
+				}
+				t.Fatalf("ms.Process(), got too many errors processing entries: %v", errs)
+			}
+
+			dir := map[string]*Entry{}
+			for _, m := range ms.Modules {
+				addTreeE(ToEntry(m), dir)
+			}
+
+			for _, tc := range tt.testcases {
+				e, ok := dir[tc.wantEntryPath]
+				if !ok {
+					t.Fatalf("could not find entry %s within the dir: %v", tc.wantEntryPath, dir)
+				}
+				tc.wantEntryCustomTest(t, e)
+			}
+		})
+	}
+}
diff --git a/pkg/yang/marshal_test.go b/pkg/yang/marshal_test.go
index f77582e..6151aa1 100644
--- a/pkg/yang/marshal_test.go
+++ b/pkg/yang/marshal_test.go
@@ -342,7 +342,8 @@ func TestMarshalJSON(t *testing.T) {
       "ListAttr": {
         "MinElements": 0,
         "MaxElements": 18446744073709551615,
-        "OrderedBy": null
+        "OrderedBy": null,
+        "OrderedByUser": false
       }
     }
   },
@@ -356,7 +357,8 @@ func TestMarshalJSON(t *testing.T) {
   "ListAttr": {
     "MinElements": 48,
     "MaxElements": 42,
-    "OrderedBy": null
+    "OrderedBy": null,
+    "OrderedByUser": false
   },
   "Identities": [
     {
@@ -534,7 +536,8 @@ func TestParseAndMarshal(t *testing.T) {
           "ListAttr": {
             "MinElements": 10,
             "MaxElements": 18446744073709551615,
-            "OrderedBy": null
+            "OrderedBy": null,
+            "OrderedByUser": false
           }
         },
         "d": {
@@ -623,7 +626,8 @@ func TestParseAndMarshal(t *testing.T) {
           "ListAttr": {
             "MinElements": 0,
             "MaxElements": 18446744073709551615,
-            "OrderedBy": null
+            "OrderedBy": null,
+            "OrderedByUser": false
           }
         },
         "zip2": {
@@ -645,7 +649,8 @@ func TestParseAndMarshal(t *testing.T) {
           "ListAttr": {
             "MinElements": 0,
             "MaxElements": 1000,
-            "OrderedBy": null
+            "OrderedBy": null,
+            "OrderedByUser": false
           }
         }
       }