From a70395cd59e07a03ad003fa0cf166e1237151cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 25 Mar 2013 11:44:18 +0100 Subject: Introduce a new mechanism for structured error handling --- lib/asn1/src/asn1ct.erl | 21 +++++++++-- lib/asn1/src/asn1ct_check.erl | 82 +++++++++++++++---------------------------- lib/asn1/test/Makefile | 3 +- lib/asn1/test/asn1_SUITE.erl | 6 ++-- lib/asn1/test/error_SUITE.erl | 73 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 59 deletions(-) create mode 100644 lib/asn1/test/error_SUITE.erl (limited to 'lib/asn1') diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index b41af6ab1b..15aa4efe7d 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -301,8 +301,10 @@ run_passes_1([{pass,Name,Pass}|Passes], #st{run=Run}=St0) try Run(Name, Pass, St0) of {ok,St} -> run_passes_1(Passes, St); - {error,#st{error=Error}} -> - {error,Error}; + {error,#st{error=Errors}} -> + {Structured,AllErrors} = clean_errors(Errors), + print_structured_errors(Structured), + {error,AllErrors}; done -> ok catch @@ -315,6 +317,21 @@ run_passes_1([{pass,Name,Pass}|Passes], #st{run=Run}=St0) run_passes_1([], _St) -> ok. +clean_errors(Errors) when is_list(Errors) -> + F = fun({structured_error,_,_,_}) -> true; + (_) -> false + end, + {Structured0,AdHoc} = lists:partition(F, Errors), + Structured = lists:sort(Structured0), + {Structured,Structured ++ AdHoc}; +clean_errors(AdHoc) -> {[],AdHoc}. + +print_structured_errors([_|_]=Errors) -> + _ = [io:format("~ts:~w: ~ts\n", [F,L,M:format_error(E)]) || + {structured_error,{F,L},M,E} <- Errors], + ok; +print_structured_errors(_) -> ok. + compile1(File, #st{opts=Opts}=St0) -> verbose("Erlang ASN.1 version ~p, compiling ~p~n", [?vsn,File], Opts), verbose("Compiler Options: ~p~n", [Opts], Opts), diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index bb0469d7a5..3c9e0dd8d2 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -25,7 +25,7 @@ %-compile(export_all). %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). --export([check/2,storeindb/2]). +-export([check/2,storeindb/2,format_error/1]). %-define(debug,1). -include("asn1_records.hrl"). %%% The tag-number for universal types @@ -175,8 +175,9 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> {NewTypes,NewValues,ParameterizedTypes,NewClasses, lists:subtract(NewObjects,ExclO)++InlinedObjects, lists:subtract(NewObjectSets,ExclOS)++ParObjectSetNames}}; - _ ->{error,{asn1,lists:flatten([Terror3,Verror5,Cerror, - Oerror,Exporterror,ImportError])}} + _ -> + {error,lists:flatten([Terror3,Verror5,Cerror, + Oerror,Exporterror,ImportError])} end. context_switch_in_spec() -> @@ -6946,60 +6947,27 @@ storeindb(S,M) when is_record(M,module) -> NewM = M#module{typeorval=findtypes_and_values(TVlist)}, asn1_db:dbnew(NewM#module.name), asn1_db:dbput(NewM#module.name,'MODULE', NewM), - Res = storeindb(NewM#module.name,TVlist,[]), + Res = storeindb(#state{mname=NewM#module.name}, TVlist, []), include_default_class(S,NewM#module.name), include_default_type(NewM#module.name), Res. -storeindb(Module,[H|T],ErrAcc) when is_record(H,typedef) -> - storeindb(Module,H#typedef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,valuedef) -> - storeindb(Module,H#valuedef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,ptypedef) -> - storeindb(Module,H#ptypedef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,classdef) -> - storeindb(Module,H#classdef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,pvaluesetdef) -> - storeindb(Module,H#pvaluesetdef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,pobjectdef) -> - storeindb(Module,H#pobjectdef.name,H,T,ErrAcc); -storeindb(Module,[H|T],ErrAcc) when is_record(H,pvaluedef) -> - storeindb(Module,H#pvaluedef.name,H,T,ErrAcc); -storeindb(_,[],[]) -> ok; -storeindb(_,[],ErrAcc) -> - {error,ErrAcc}. - -storeindb(Module,Name,H,T,ErrAcc) -> - case asn1_db:dbget(Module,Name) of +storeindb(#state{mname=Module}=S, [H|T], Errors) -> + Name = asn1ct:get_name_of_def(H), + case asn1_db:dbget(Module, Name) of undefined -> - asn1_db:dbput(Module,Name,H), - storeindb(Module,T,ErrAcc); - _ -> - case H of - _Type when is_record(H,typedef) -> - error({type,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,valuedef) -> - error({value,"already defined", - #state{mname=Module,value=H,vname=Name}}); - _Type when is_record(H,ptypedef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,pobjectdef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,pvaluesetdef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,pvaluedef) -> - error({ptype,"already defined", - #state{mname=Module,type=H,tname=Name}}); - _Type when is_record(H,classdef) -> - error({class,"already defined", - #state{mname=Module,value=H,vname=Name}}) - end, - storeindb(Module,T,[H|ErrAcc]) - end. + asn1_db:dbput(Module, Name, H), + storeindb(S, T, Errors); + Prev -> + PrevLine = asn1ct:get_pos_of_def(Prev), + {error,Error} = asn1_error(S, H, {already_defined,Name,PrevLine}), + storeindb(S, T, [Error|Errors]) + end; +storeindb(_, [], []) -> + ok; +storeindb(_, [], [_|_]=Errors) -> + {error,Errors}. + findtypes_and_values(TVList) -> findtypes_and_values(TVList,[],[],[],[],[],[]).%% Types,Values, @@ -7039,7 +7007,15 @@ findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) -> {lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc), lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}. - +asn1_error(#state{mname=Where}, Item, Error) -> + Pos = asn1ct:get_pos_of_def(Item), + {error,{structured_error,{Where,Pos},?MODULE,Error}}. + +format_error({already_defined,Name,PrevLine}) -> + io_lib:format("the name ~p has already been defined at line ~p", + [Name,PrevLine]); +format_error(Other) -> + io_lib:format("~p", [Other]). error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) -> Pos = Ref#'Externaltypereference'.pos, diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile index 6e6ab4639c..15b97df972 100644 --- a/lib/asn1/test/Makefile +++ b/lib/asn1/test/Makefile @@ -114,7 +114,8 @@ MODULES= \ asn1_app_test \ asn1_appup_test \ asn1_wrapper \ - asn1_SUITE + asn1_SUITE \ + error_SUITE SUITE= asn1_SUITE.erl diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 5ec201fd78..2b3ff07980 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -860,7 +860,7 @@ testInvokeMod(Config, Rule, Opts) -> {ok, _Result2} = 'PrimStrings':encode('Bs1', [1, 0, 1, 0]). testExport(Config) -> - {error, {asn1, _Reason}} = + {error, _} = asn1ct:compile(filename:join(?config(data_dir, Config), "IllegalExport"), [{outdir, ?config(case_dir, Config)}]). @@ -906,8 +906,8 @@ testOpenTypeImplicitTag(Config, Rule, Opts) -> duplicate_tags(Config) -> DataDir = ?config(data_dir, Config), CaseDir = ?config(case_dir, Config), - {error, {asn1, [{error, {type, _, _, 'SeqOpt1Imp', - {asn1, {duplicates_of_the_tags, _}}}}]}} = + {error, [{error, {type, _, _, 'SeqOpt1Imp', + {asn1, {duplicates_of_the_tags, _}}}}]} = asn1ct:compile(filename:join(DataDir, "SeqOptional2"), [abs, {outdir, CaseDir}]). diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl new file mode 100644 index 0000000000..4dd4d58aad --- /dev/null +++ b/lib/asn1/test/error_SUITE.erl @@ -0,0 +1,73 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(error_SUITE). +-export([suite/0,all/0,groups/0, + already_defined/1]). + +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group,p}]. + +groups() -> + [{p,parallel(),[already_defined]}]. + +parallel() -> + case erlang:system_info(schedulers) > 1 of + true -> [parallel]; + false -> [] + end. + +already_defined(Config) -> + M = 'Already', + P = {M, + <<"Already DEFINITIONS ::= BEGIN\n" + " I ::= INTEGER\n" + " i I ::= 42\n" + " I ::= OCTET STRING\n" + " I ::= CLASS { &Type }\n" + " MYCLASS ::= CLASS { &Type }\n" + " i MYCLASS ::= { &Type INTEGER }\n" + " o MYCLASS ::= { &Type INTEGER }\n" + " I MYCLASS ::= { o }\n" + " I{T} ::= SEQUENCE OF T\n" + " I{INTEGER:x} INTEGER ::= { 1 | 2 | x }\n" + " i{T} MYCLASS ::= { &Type T }\n" + "END\n">>}, + {error, + [ + {structured_error,{M,4},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,5},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,7},asn1ct_check,{already_defined,'i',3}}, + {structured_error,{M,9},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,10},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,11},asn1ct_check,{already_defined,'I',2}}, + {structured_error,{M,12},asn1ct_check,{already_defined,'i',3}} + ] + } = run(P, Config), + ok. + +run({Mod,Spec}, Config) -> + Base = atom_to_list(Mod) ++ ".asn1", + File = filename:join(?config(priv_dir, Config), Base), + ok = file:write_file(File, Spec), + asn1ct:compile(File). -- cgit v1.2.3