diff --git a/include/yang.hrl b/include/yang.hrl index f9516e2..2ec0c31 100644 --- a/include/yang.hrl +++ b/include/yang.hrl @@ -55,6 +55,7 @@ name :: atom(), %% moduleref is the name and revision where this item is defined moduleref :: yang:modrev(), + status :: 'undefined' | yang:yang_status(), v_status :: yang:validate_status(), stmt :: yang:stmt(), pmap = yang:map_new() :: yang:map0() % used by plugins diff --git a/src/yang.erl b/src/yang.erl index 37a2aff..56ec322 100644 --- a/src/yang.erl +++ b/src/yang.erl @@ -1322,7 +1322,7 @@ mk_module_maps([{Kwd, Arg, _, Substmts} = S | Stmts], add_to_definitions_map(Arg, I, #identity.stmt, Is, Ctx0), mk_module_maps(Stmts, Ts, Gs, Is1, Fs, Es, M, Ctx1); 'feature' -> - F = #feature{name = Arg, stmt = S, moduleref = MRef}, + F = #feature{name = Arg, stmt = S, status = Status, moduleref = MRef}, {Fs1, Ctx1} = add_to_definitions_map(Arg, F, #feature.stmt, Fs, Ctx0), mk_module_maps(Stmts, Ts, Gs, Is, Fs1, Es, M, Ctx1); @@ -1553,17 +1553,18 @@ add_feature(F0, Map0, M, Ctx0) -> undefined -> %% unhandled statement Map1 = map_update(Name, F0#feature{v_status = processing}, Map0), + Status = get_stmts_arg(Substmts, 'status', 'current'), {Map2, Ctx1} = iterate_stmt( fun(RefStmt, {Map, Ctx}) -> - {continue, chk_if_feature(RefStmt, Map, M, Ctx)} + {continue, chk_if_feature(RefStmt, Map, M, Status, Ctx)} end, 'if-feature', {Map1, Ctx0}, Substmts), F1 = F0#feature{v_status = done}, Map3 = map_update(Name, F1, Map2), {Map3, Ctx1} end. -chk_if_feature({_, RefArg, Pos, _}, Map0, M, Ctx0) -> +chk_if_feature({_, RefArg, Pos, _}, Map0, M, Status, Ctx0) -> case get_feature_expr0(RefArg, Pos, M#module{features = Map0}, Ctx0, _DoResolve = false) @@ -1576,7 +1577,16 @@ chk_if_feature({_, RefArg, Pos, _}, Map0, M, Ctx0) -> fun(Ref, {Map1, Ctx2}) -> case get_feature(Ref, Pos, Map1, M, Ctx2) of {Feature, Ctx3} when is_record(Feature, feature) -> - add_feature(Feature, Map1, M, Ctx3); + case resolve_raw_idref(Ref, Pos, M, Ctx2) of + {self, _, _} -> + RefStatus = Feature#feature.status, + Ctx4 = + chk_status(Status, RefStatus, 'feature', 'feature', + Pos, Ctx3), + add_feature(Feature, Map1, M, Ctx4); + _ -> + add_feature(Feature, Map1, M, Ctx3) + end; {undefined, Ctx3} -> {Map1, Ctx3} end @@ -2152,7 +2162,12 @@ mk_children([{Kwd, Arg, Pos, Substmts} = Stmt | T], GroupingMap0, end, {FeatureL, WhenL, MustL, Status, Ctx1} = common_substmts(Substmts, 'local', M, Ctx0), - {IfFeatureRes, Ctx2} = check_if_features(FeatureL, M, Ctx1), + {IfFeatureRes, Ctx1_1} = check_if_features(FeatureL, M, Ctx1), + if IfFeatureRes == error -> + Ctx2 = Ctx1_1; + true -> + Ctx2 = validate_features_status(FeatureL, M, Status, Ctx1_1, Kind) + end, Sn0 = #sn{name = Name, kind = Kind, module = M, @@ -2503,6 +2518,28 @@ check_if_features(Features, M, #yctx{features = FMap} = Ctx) -> Error end. +validate_features_status([{Expr, _, Stmt} | T], M, Status, Ctx, Kind) -> + Ctx1 = + lists:foldl( + fun(Feature, Acc) -> + validate_feature_status(Feature, Stmt, M, Status, Acc, Kind) + end, Ctx, + yang_if_feature:get_features(Expr)), + validate_features_status(T, M, Status, Ctx1, Kind); + +validate_features_status([], _, _, Ctx1, _) -> + Ctx1. + +validate_feature_status({ModName, Name}, Stmt, M, Status, Ctx, Kind) -> + if ModName == M#module.modulename -> + Map = M#module.features, + {value, Definition} = map_lookup(Name, Map), + chk_status(Status, Definition#feature.status, + Kind, 'feature', stmt_pos(Stmt), Ctx); + true -> + Ctx + end. + validate_features_exist(Features, M, Ctx) -> validate_features_exist0(Features, M, true, Ctx). diff --git a/src/yang_types.erl b/src/yang_types.erl index 4c68cf8..49de136 100644 --- a/src/yang_types.erl +++ b/src/yang_types.erl @@ -703,11 +703,23 @@ enumeration_type_spec_fun({parse, _Val, _Pos}, {undefined, Ctx}; enumeration_type_spec_fun({parse, Val, _Pos}, #type{type_spec = - #enumeration_type_spec{enums = Enums}} = Type, + #enumeration_type_spec{all_enums = AllEnums, all_enum_stmts = AllStmts}} = Type, _M, _Ctx) -> - case lists:keyfind(?b2a(Val), 1, Enums) of + case lists:keyfind(?b2a(Val), 1, AllEnums) of {_, IntVal} -> - {ok, IntVal}; + case lists:keyfind(?b2a(Val), 2, AllStmts) of + {_, _, _, IfFeatureStmts} -> + case lists:keyfind('if-feature', 1, IfFeatureStmts) of + {_, _, _, _} -> + {error, + "a 'default' value cannot be given in leaf node when 'if-feature' is existing", + stmt_pos(Type#type.stmt)}; + false -> + {ok, IntVal} + end; + false -> + {ok, IntVal} + end; false -> {error, "enum not defined for enumeration", @@ -922,23 +934,38 @@ bits_type_spec_fun({parse, _Val, _Pos}, %% no bits, parsing will always fail, error already reported {undefined, Ctx}; bits_type_spec_fun({parse, Val, _Pos}, - #type{type_spec = #bits_type_spec{bits = Bits}} = Type, + #type{type_spec = #bits_type_spec{all_bits = AllBits, all_bit_stmts = AllStmts}} = Type, _M, _Ctx) -> BinNames = re:split(Val, "\\s+", [{return, binary}]), F = fun (<<>>, Acc) -> Acc; (Name, Acc) -> - {_, BitPos} = lists:keyfind(?b2a(Name), 1, Bits), - [BitPos | Acc] + case lists:keyfind(?b2a(Name), 1, AllBits) of + {_, BitPos} -> + case lists:keyfind(?b2a(Name), 2, AllStmts) of + {_, _, _, BitStmt} -> + case lists:keymember('if-feature', 1, BitStmt) of + true -> + throw(default_bit_if_feature_error); + false -> + [BitPos | Acc] + end; + false -> + [BitPos | Acc] + end; + false -> + throw(bit_not_defined) + end end, try {ok, lists:foldl(F, [], BinNames)} catch - _:_ -> + throw:default_bit_if_feature_error -> + {error, "a 'default' value cannot be given in leaf node when 'if-feature' is existing", stmt_pos(Type#type.stmt)}; + throw:bit_not_defined -> {error, "bit not defined for bits type", stmt_pos(Type#type.stmt)} end. - parse_bits(Bitstmts, Pos, M, Ctx0, BaseBitsAndStmts) -> {Bits, Stmts, AllBits, AllStmts, Ctx1} = parse_names_and_values( diff --git a/test/lux/bad/enum-bit-default.yang b/test/lux/bad/enum-bit-default.yang new file mode 100644 index 0000000..c9d0ee9 --- /dev/null +++ b/test/lux/bad/enum-bit-default.yang @@ -0,0 +1,103 @@ +module enum-bit-default { + yang-version 1.1; + namespace urn:enum-bit-default; + prefix ebd; + + feature foo; + feature bar; + feature baz; + + typedef myEnum { + type enumeration { + enum foo { + if-feature "foo"; + } + enum bar { + if-feature "not bar"; + } + enum baz; + } + } + + typedef myBits { + type bits { + bit foo { + if-feature "foo"; + } + bit bar { + if-feature "not bar"; + } + bit baz; + } + } + + leaf enum0 { + type myEnum; + default "no"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf enum1 { + type myEnum; + default "foo"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf enum2 { + type myEnum; + default "bar"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf enum3 { + type myEnum; + default "baz"; + } + + leaf bit0 { + type myBits; + default "no"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit1 { + type myBits; + default "foo"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit2 { + type myBits; + default "bar"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit3 { + type myBits; + default "baz"; + } + + leaf bit4 { + type myBits; + default "no foo"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit5 { + type myBits; + default "foo no"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit6 { + type myBits; + default "no baz"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit7 { + type myBits; + default "baz no"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit8 { + type myBits; + default "foo bar baz"; // LINE: YANG_ERR_TYPE_VALUE + } + + leaf bit9 { + type myBits; + default "baz bar foo"; // LINE: YANG_ERR_TYPE_VALUE + } +} diff --git a/test/lux/bad/feat-status.yang b/test/lux/bad/feat-status.yang new file mode 100644 index 0000000..bf2b351 --- /dev/null +++ b/test/lux/bad/feat-status.yang @@ -0,0 +1,54 @@ +module feat-status { + yang-version 1.1; + namespace urn:feat-status; + prefix fs; + + feature f; + + feature f1 { + status obsolete; + } + + feature f2 { + if-feature "f"; + } + + feature f3 { + if-feature "no"; // LINE: YANG_ERR_DEFINITION_NOT_FOUND + } + + feature f4 { + if-feature "fs:f1"; // LINE: YANG_BAD_STATUS_REFERENCE + } + + feature f5 { + if-feature "not fs:f1 or f"; // LINE: YANG_BAD_STATUS_REFERENCE + } + + feature f6 { + status deprecated; + if-feature f1; // LINE: YANG_BAD_STATUS_REFERENCE + } + + leaf l { + type empty; + if-feature "f"; + } + + leaf l2 { + type empty; + if-feature "no"; // LINE: YANG_ERR_DEFINITION_NOT_FOUND + } + + leaf l3 { + type empty; + if-feature "f1"; // LINE: YANG_BAD_STATUS_REFERENCE + } + + container c { + if-feature "f1"; // LINE: YANG_BAD_STATUS_REFERENCE + leaf l3 { + type empty; + } + } +}