File 2813-parsetools-Let-Leex-recognize-ERL_COMPILER_OPTIONS.patch of Package erlang

From 31a7aeed186bd6c2113e7d9b5b59d996be4629a3 Mon Sep 17 00:00:00 2001
From: Hans Bolinder <hasse@erlang.org>
Date: Tue, 24 Nov 2020 09:24:24 +0100
Subject: [PATCH 3/3] parsetools: Let Leex recognize ERL_COMPILER_OPTIONS

---
 lib/parsetools/doc/src/leex.xml    |  16 +++
 lib/parsetools/src/leex.erl        | 161 +++++++++++++++++------------
 lib/parsetools/test/leex_SUITE.erl |  31 +++++-
 3 files changed, 142 insertions(+), 66 deletions(-)

diff --git a/lib/parsetools/doc/src/leex.xml b/lib/parsetools/doc/src/leex.xml
index 0e69e44667..153374e16a 100644
--- a/lib/parsetools/doc/src/leex.xml
+++ b/lib/parsetools/doc/src/leex.xml
@@ -241,6 +241,22 @@ io:request(InFile, {get_until,unicode,Prompt,Module,tokens,[Line]})
     </func>
   </funcs>
 
+  <section>
+    <title>Default Leex Options</title>
+    <p>The (host operating system) environment variable
+      <c>ERL_COMPILER_OPTIONS</c> can be used to give default Leex
+      options. Its value must be a valid Erlang term. If the value is a
+      list, it is used as is. If it is not a list, it is put
+      into a list.</p>
+
+      <p>The list is appended to any options given to
+      <seemfa marker="#file/2">file/2</seemfa>.</p>
+
+      <p>The list can be retrieved with
+      <seemfa marker="compiler:compile#env_compiler_options/0">
+      compile:env_compiler_options/0</seemfa>.</p>
+  </section>
+
   <section>
     <title>Input File Format</title>
     <p>Erlang style comments starting with a <c>%</c> are allowed in
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index f60ad76175..90d1b9f71e 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -34,9 +34,8 @@
 
 -export([compile/3,file/1,file/2,format_error/1]).
 
--import(lists, [member/2,reverse/1,sort/1,delete/2,
-                keysort/2,keydelete/3,
-                map/2,foldl/3,foreach/2,flatmap/2]).
+-import(lists, [member/2,reverse/1,sort/1,keysort/2,
+                map/2,foldl/3,foldr/3,foreach/2,flatmap/2]).
 -import(ordsets, [is_element/2,add_element/2,union/2]).
 -import(orddict, [store/3]).
 
@@ -123,12 +122,15 @@ file(File) -> file(File, []).
               | 'return_errors' | 'return_warnings' | 'return'
               | 'verbose' | 'warnings_as_errors'.
 
-file(File, Opts0) ->
+file(File, Opts0) when is_list(Opts0) ->
     case is_filename(File) of
         no -> erlang:error(badarg, [File,Opts0]);
         _ -> ok
     end,
-    Opts = case options(Opts0) of
+    EnvOpts0 = env_default_opts(),
+    EnvOpts = select_recognized_opts(EnvOpts0),
+    Opts1 = Opts0 ++ EnvOpts,
+    Opts = case options(Opts1) of
                badarg ->
                    erlang:error(badarg, [File,Opts0]);
                Options ->
@@ -152,7 +154,9 @@ file(File, Opts0) ->
          catch #leex{}=St4 ->
              St4
          end,
-    leex_ret(St).             
+    leex_ret(St);
+file(File, Opt) ->
+    file(File, [Opt]).
 
 -spec format_error(ErrorDescriptor) -> io_lib:chars() when
       ErrorDescriptor :: term().
@@ -196,83 +200,112 @@ strip_extension(File, Ext) ->
         _Other -> File
     end.
 
-options(Options0) when is_list(Options0) ->
-    try 
-        Options = flatmap(fun(return) -> short_option(return, true);
-                             (report) -> short_option(report, true);
-                             ({return,T}) -> short_option(return, T);
-                             ({report,T}) -> short_option(report, T);
-                             (T) -> [T]
-                          end, Options0),
-        options(Options, [scannerfile,includefile,report_errors,
-                          report_warnings,warnings_as_errors,
-                          return_errors,return_warnings,
-                          verbose,dfa_graph], [])
-    catch error: _ -> badarg
-    end;
-options(Option) ->
-    options([Option]).
-
-short_option(return, T) ->
-    [{return_errors,T}, {return_warnings,T}];
-short_option(report, T) ->
-    [{report_errors,T}, {report_warnings,T}].
-
-options(Options0, [Key|Keys], L) when is_list(Options0) ->
-    Options = case member(Key, Options0) of
-                  true -> 
-                      [atom_option(Key)|delete(Key, Options0)];
-                  false ->
-                      Options0
-              end,
-    V = case lists:keyfind(Key, 1, Options) of
-            {Key, Filename0} when Key =:= includefile;
-				  Key =:= scannerfile ->
-                case is_filename(Filename0) of
-                    no -> 
-                        badarg;
-                    Filename -> 
-                        {ok,[{Key,Filename}]}
-                end;
-            {Key, Bool} = KB when is_boolean(Bool) ->
-                {ok, [KB]};
-            {Key, _} ->
-                badarg;
-            false ->
-                {ok,[{Key,default_option(Key)}]}
-        end,
-    case V of
+%% Copied from compile.erl.
+env_default_opts() ->
+    Key = "ERL_COMPILER_OPTIONS",
+    case os:getenv(Key) of
+	false -> [];
+	Str when is_list(Str) ->
+	    case erl_scan:string(Str) of
+		{ok,Tokens,_} ->
+                    Dot = {dot, erl_anno:new(1)},
+		    case erl_parse:parse_term(Tokens ++ [Dot]) of
+			{ok,List} when is_list(List) -> List;
+			{ok,Term} -> [Term];
+			{error,_Reason} ->
+			    io:format("Ignoring bad term in ~s\n", [Key]),
+			    []
+		    end;
+		{error, {_,_,_Reason}, _} ->
+		    io:format("Ignoring bad term in ~s\n", [Key]),
+		    []
+	    end
+    end.
+
+select_recognized_opts(Options0) ->
+    Options = preprocess_options(Options0),
+    AllOptions = all_options(),
+    [Option ||
+        {Name, _} = Option <- Options,
+        lists:member(Name, AllOptions)].
+
+options(Options0) ->
+    Options1 = preprocess_options(Options0),
+    AllOptions = all_options(),
+    case check_options(Options1, AllOptions, []) of
         badarg ->
             badarg;
-        {ok,KeyValueL} ->
-            NewOptions = keydelete(Key, 1, Options),
-            options(NewOptions, Keys, KeyValueL ++ L)
+        OptionValues  ->
+            AllOptionValues =
+                [case lists:keyfind(Option, 1, OptionValues) of
+                     false ->
+                         {Option, default_option(Option)};
+                     OptionValue ->
+                         OptionValue
+                 end || Option <- AllOptions],
+            foldr(fun({_, false}, L) -> L;
+                     ({Option, true}, L) -> [Option | L];
+                     (OptionValue, L) -> [OptionValue | L]
+                  end, [], AllOptionValues)
+    end.
+
+preprocess_options(Options) ->
+    foldr(fun preproc_opt/2, [], Options).
+
+preproc_opt(return, Os) ->
+    [{return_errors, true}, {return_warnings, true} | Os];
+preproc_opt(report, Os) ->
+    [{report_errors, true}, {report_warnings, true} | Os];
+preproc_opt({return, T}, Os) ->
+    [{return_errors, T}, {return_warnings, T} | Os];
+preproc_opt({report, T}, Os) ->
+    [{report_errors, T}, {report_warnings, T} | Os];
+preproc_opt(Option, Os) ->
+    [try atom_option(Option) catch error:_ -> Option end | Os].
+
+check_options([{Option, FileName0} | Options], AllOptions, L)
+          when Option =:= includefile; Option =:= scannerfile ->
+    case is_filename(FileName0) of
+        no -> 
+            badarg;
+        Filename -> 
+            check_options(Options, AllOptions, [{Option, Filename} | L])
     end;
-options([], [], L) ->
-    foldl(fun({_,false}, A) -> A;
-             ({Tag,true}, A) -> [Tag|A];
-             (F,A) -> [F|A]
-          end, [], L);
-options(_Options, _, _L) ->
+check_options([{Option, Boolean} | Options], AllOptions, L)
+          when is_boolean(Boolean) ->
+    case lists:member(Option, AllOptions) of
+        true ->
+            check_options(Options, AllOptions, [{Option, Boolean} | L]);
+        false ->
+            badarg
+        end;
+check_options([], _AllOptions, L) ->
+    L;
+check_options(_Options, _, _L) ->
     badarg.
 
+all_options() ->
+    [dfa_graph,includefile,report_errors,report_warnings,
+     return_errors,return_warnings,scannerfile,verbose,
+     warnings_as_errors].
+
 default_option(dfa_graph) -> false;
 default_option(includefile) -> [];
 default_option(report_errors) -> true;
 default_option(report_warnings) -> true;
-default_option(warnings_as_errors) -> false;
 default_option(return_errors) -> false;
 default_option(return_warnings) -> false;
 default_option(scannerfile) -> [];
-default_option(verbose) -> false.
+default_option(verbose) -> false;
+default_option(warnings_as_errors) -> false.
 
 atom_option(dfa_graph) -> {dfa_graph,true};
 atom_option(report_errors) -> {report_errors,true};
 atom_option(report_warnings) -> {report_warnings,true};
 atom_option(warnings_as_errors) -> {warnings_as_errors,true};
 atom_option(return_errors) -> {return_errors,true};
-atom_option(return_warnings) -> {return_warnings,true};
 atom_option(verbose) -> {verbose,true};
+atom_option(return_warnings) -> {return_warnings,true};
 atom_option(Key) -> Key.
 
 is_filename(T) ->
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 16f67f7f26..e8ec376d4b 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -45,7 +44,7 @@
 	 pt/1, man/1, ex/1, ex2/1, not_yet/1,
 	 line_wrap/1,
 	 otp_10302/1, otp_11286/1, unicode/1, otp_13916/1, otp_14285/1,
-         compiler_warnings/1]).
+         otp_17023/1, compiler_warnings/1]).
 
 % Default timetrap timeout (set in init_per_testcase).
 -define(default_timeout, ?t:minutes(1)).
@@ -67,7 +67,7 @@ all() ->
 groups() -> 
     [{checks, [], [file, compile, syntax]},
      {examples, [], [pt, man, ex, ex2, not_yet, unicode]},
-     {tickets, [], [otp_10302, otp_11286, otp_13916, otp_14285,
+     {tickets, [], [otp_10302, otp_11286, otp_13916, otp_14285, otp_17023,
                     compiler_warnings]},
      {bugs, [], [line_wrap]}].
 
@@ -1185,6 +1185,32 @@ otp_14285(Config) ->
     {ok, compiler_warnings, []} = compile:file(ErlFile, [return]),
     ok.
 
+otp_17023(Config) ->
+    Dir = ?privdir,
+    Filename = filename:join(Dir, "file.xrl"),
+    Ret = [return, {report, true}],
+
+    {'EXIT', {badarg, _}} = (catch leex:file(Filename, [{noopt,true}])),
+    OldEnv = os:getenv("ERL_COMPILER_OPTIONS"),
+    true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"),
+    ok = file:write_file(Filename,
+                               <<"Definitions.\n"
+                                 "Rules.\n"
+                                 "^ : .\n"
+                                 "Erlang code.\n">>),
+    {error,[{_,[{3,leex,{regexp,_}}]}],[]} = 
+        leex:file(Filename, Ret),
+    true = os:putenv("ERL_COMPILER_OPTIONS", "{return, false}"),
+    error = leex:file(Filename, Ret),
+    error = leex:file(Filename, [return | Ret]), % overridden
+    case OldEnv of
+        false ->
+            os:unsetenv("ERL_COMPILER_OPTIONS");
+        _ ->
+            os:putenv("ERL_COMPILER_OPTIONS", OldEnv)
+    end,
+    ok.
+
 start_node(Name, Args) ->
     [_,Host] = string:tokens(atom_to_list(node()), "@"),
     ct:log("Trying to start ~w@~s~n", [Name,Host]),
-- 
2.26.2

openSUSE Build Service is sponsored by