Skip to content

Commit 513d482

Browse files
committed
Make options in the -compile attribute take precedence
Change the compiler option processing order so that options given in the `compile()` attribute takes precedence over options given to the compiler, which in turn takes precedence over options given in the environment. This order makes most sense, as each module might need customized options. While at it, remove the undocumented `strict_record_updates` / `no_strict_record_updates` options. Their naming no longer make any sense, because record updates are always strict (that is, the source record must have the correct tag and size). Incorporate the behavior of `strict_record_updates` to update the record by matching and building a new tuple into the `dialyzer` option. When dialyzer is not used, records are updated using `setelement/3`, which is more efficient in the JIT. (This is the second attempt to fix erlang#6979, as erlang#8093 did not really work.)
1 parent ab7b354 commit 513d482

File tree

5 files changed

+297
-35
lines changed

5 files changed

+297
-35
lines changed

lib/compiler/src/compile.erl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ compiler recursively from inside a parse transform.
4343

4444
The list can be retrieved with `env_compiler_options/0`.
4545

46+
## Order of Compiler Options
47+
48+
Options given in the `compile()` attribute in the source code takes
49+
precedence over options given to the compiler, which in turn takes
50+
precedence over options given in the environment.
51+
52+
A later compiler option takes precedence over an earlier one in the
53+
option list. Example:
54+
55+
```
56+
compile:file(something, [nowarn_missing_spec,warn_missing_spec]).
57+
```
58+
59+
Warnings will be emitted for functions without specifications, unless
60+
the source code for module `something` contains a `compile(nowarn_missing_spec)`
61+
attribute.
62+
4663
## Inlining
4764

4865
The compiler can do function inlining within an Erlang
@@ -792,7 +809,7 @@ Module:format_error(ErrorDescriptor)
792809
CompRet :: comp_ret().
793810

794811
file(File, Opts) when is_list(Opts) ->
795-
do_compile({file,File}, Opts++env_default_opts());
812+
do_compile({file,File}, env_default_opts() ++ Opts);
796813
file(File, Opt) ->
797814
file(File, [Opt|?DEFAULT_OPTIONS]).
798815

lib/compiler/test/compile_SUITE.erl

Lines changed: 247 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
bc_options/1, deterministic_include/1, deterministic_paths/1,
4040
compile_attribute/1, message_printing/1, other_options/1,
4141
transforms/1, erl_compile_api/1, types_pp/1, bs_init_writable/1,
42-
annotations_pp/1
42+
annotations_pp/1, option_order/1
4343
]).
4444

4545
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -59,7 +59,8 @@ all() ->
5959
env_compiler_options, custom_debug_info, bc_options,
6060
custom_compile_info, deterministic_include, deterministic_paths,
6161
compile_attribute, message_printing, other_options, transforms,
62-
erl_compile_api, types_pp, bs_init_writable, annotations_pp].
62+
erl_compile_api, types_pp, bs_init_writable, annotations_pp,
63+
option_order].
6364

6465
groups() ->
6566
[].
@@ -906,10 +907,10 @@ strict_record(Config) when is_list(Config) ->
906907
{ok,M} = c:c(M, [no_strict_record_tests|Opts]),
907908
Turtle = test_sloppy(),
908909

909-
%% The option first given wins.
910-
{ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]),
911-
Turtle = test_sloppy(),
910+
%% The option last given wins.
912911
{ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]),
912+
Turtle = test_sloppy(),
913+
{ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]),
913914
Turtle = test_strict(),
914915

915916
%% Default (possibly influenced by ERL_COMPILER_OPTIONS).
@@ -2120,6 +2121,146 @@ get_annotations(Key, [_|Lines]) ->
21202121
get_annotations(_, []) ->
21212122
[].
21222123

2124+
option_order(Config) ->
2125+
Ts = [{spec1,
2126+
~"""
2127+
-compile(nowarn_missing_spec).
2128+
foo() -> ok.
2129+
""",
2130+
[], %Environment
2131+
[warn_missing_spec],
2132+
[]},
2133+
{spec2,
2134+
~"""
2135+
foo() -> ok.
2136+
""",
2137+
[{"ERL_COMPILER_OPTIONS", "warn_missing_spec"}],
2138+
[nowarn_missing_spec],
2139+
[]},
2140+
{spec3,
2141+
~"""
2142+
-compile(nowarn_missing_spec).
2143+
foo() -> ok.
2144+
""",
2145+
[{"ERL_COMPILER_OPTIONS", "nowarn_missing_spec"}],
2146+
[warn_missing_spec],
2147+
[]},
2148+
{spec4,
2149+
~"""
2150+
-compile(warn_missing_spec).
2151+
foo() -> ok.
2152+
""",
2153+
[{"ERL_COMPILER_OPTIONS", "nowarn_missing_spec"}],
2154+
[],
2155+
{warnings,[{{2,1},erl_lint,{missing_spec,{foo,0}}}]}
2156+
},
2157+
{spec5,
2158+
~"""
2159+
-compile([warn_missing_spec,nowarn_missing_spec]).
2160+
foo() -> ok.
2161+
""",
2162+
[{"ERL_COMPILER_OPTIONS", "nowarn_missing_spec"}],
2163+
[warn_missing_spec],
2164+
[]},
2165+
{records1,
2166+
~"""
2167+
-record(r, {x,y}).
2168+
rec_test(#r{x=X,y=Y}) -> X + Y.
2169+
""",
2170+
[],
2171+
[strict_record_tests],
2172+
fun(M) ->
2173+
try M:rec_test({r,1,2,3}) of
2174+
3 ->
2175+
fail()
2176+
catch
2177+
error:function_clause ->
2178+
ok
2179+
end
2180+
end},
2181+
{records2,
2182+
~"""
2183+
-record(r, {x,y}).
2184+
rec_test(R) -> R#r.x + R#r.y.
2185+
""",
2186+
[],
2187+
[no_strict_record_tests],
2188+
fun(M) ->
2189+
3 = M:rec_test({r,1,2,3}),
2190+
ok
2191+
end},
2192+
{records3,
2193+
~"""
2194+
-compile(no_strict_record_tests).
2195+
-record(r, {x,y}).
2196+
rec_test(R) -> R#r.x + R#r.y.
2197+
""",
2198+
[],
2199+
[strict_record_tests],
2200+
fun(M) ->
2201+
3 = M:rec_test({r,1,2,3}),
2202+
ok
2203+
end},
2204+
{records4,
2205+
~"""
2206+
-record(r, {x,y}).
2207+
rec_test(#r{x=X,y=Y}) -> X + Y.
2208+
""",
2209+
[{"ERL_COMPILER_OPTIONS", "strict_record_tests"}],
2210+
[],
2211+
fun(M) ->
2212+
try M:rec_test({r,1,2,3}) of
2213+
3 ->
2214+
fail()
2215+
catch
2216+
error:function_clause ->
2217+
ok
2218+
end
2219+
end},
2220+
{records5,
2221+
~"""
2222+
-record(r, {x,y}).
2223+
rec_test(R) -> R#r.x + R#r.y.
2224+
""",
2225+
[{"ERL_COMPILER_OPTIONS", "strict_record_tests"}],
2226+
[no_strict_record_tests],
2227+
fun(M) ->
2228+
3 = M:rec_test({r,1,2,3}),
2229+
ok
2230+
end},
2231+
{records6,
2232+
~"""
2233+
-compile(no_strict_record_tests).
2234+
-record(r, {x,y}).
2235+
rec_test(R) -> R#r.x + R#r.y.
2236+
""",
2237+
[{"ERL_COMPILER_OPTIONS", "strict_record_tests"}],
2238+
[],
2239+
fun(M) ->
2240+
3 = M:rec_test({r,1,2,3}),
2241+
ok
2242+
end},
2243+
{records7,
2244+
~"""
2245+
-record(r, {x,y}).
2246+
rec_test(R) -> R#r.x + R#r.y.
2247+
""",
2248+
[{"ERL_COMPILER_OPTIONS", "no_strict_record_tests"}],
2249+
[no_strict_record_tests, strict_record_tests],
2250+
fun(M) ->
2251+
try M:rec_test({r,1,2,3}) of
2252+
3 ->
2253+
fail()
2254+
catch
2255+
error:{badrecord,{r,1,2,3}} ->
2256+
ok
2257+
end
2258+
end}
2259+
2260+
],
2261+
run(Config, Ts),
2262+
ok.
2263+
21232264
%%%
21242265
%%% Utilities.
21252266
%%%
@@ -2149,3 +2290,104 @@ is_lfe_module(File, Ext) ->
21492290
"lfe_" ++ _ -> true;
21502291
_ -> false
21512292
end.
2293+
2294+
%% Compiles a test module and returns the list of errors and warnings.
2295+
2296+
run(Config, Tests) ->
2297+
F = fun({N,P,Env,Ws,Run}, _BadL) when is_function(Run, 1) ->
2298+
case catch run_test(Config, P, Env, Ws, Run) of
2299+
ok ->
2300+
ok;
2301+
Bad ->
2302+
io:format("~nTest ~p failed. Expected~n ~p~n"
2303+
"but got~n ~p~n", [N, ok, Bad]),
2304+
fail()
2305+
end;
2306+
({N,P,Env,Ws,Expected}, BadL)
2307+
when is_list(Expected); is_tuple(Expected) ->
2308+
io:format("### ~s\n", [N]),
2309+
case catch run_test(Config, P, Env, Ws, none) of
2310+
Expected ->
2311+
BadL;
2312+
Bad ->
2313+
io:format("~nTest ~p failed. Expected~n ~p~n"
2314+
"but got~n ~p~n", [N, Expected, Bad]),
2315+
fail()
2316+
end
2317+
end,
2318+
lists:foldl(F, [], Tests).
2319+
2320+
run_test(Conf, Test0, Env, Options, Run) ->
2321+
run_test_putenv(Env),
2322+
Module = "warnings" ++ test_lib:uniq(),
2323+
Filename = Module ++ ".erl",
2324+
DataDir = proplists:get_value(priv_dir, Conf),
2325+
Test1 = ["-module(", Module, "). -file( \"", Filename, "\", 1). ", Test0],
2326+
Test = iolist_to_binary(Test1),
2327+
File = filename:join(DataDir, Filename),
2328+
Opts = [binary,export_all,return|Options],
2329+
ok = file:write_file(File, Test),
2330+
2331+
%% Compile once just to print all warnings (and cover more code).
2332+
_ = compile:file(File, [binary,export_all,report|Options]),
2333+
2334+
%% Test result of compilation.
2335+
{ok, Mod, Beam, Warnings} = compile:file(File, Opts),
2336+
_ = file:delete(File),
2337+
2338+
if
2339+
is_function(Run, 1) ->
2340+
{module,Mod} = code:load_binary(Mod, "", Beam),
2341+
ok = Run(Mod),
2342+
run_test_unsetenv(Env),
2343+
true = code:delete(Mod),
2344+
_ = code:purge(Mod),
2345+
ok;
2346+
Run =:= none ->
2347+
run_test_unsetenv(Env),
2348+
Res = get_warnings(Warnings),
2349+
case Res of
2350+
[] ->
2351+
[];
2352+
{warnings, Ws} ->
2353+
print_warnings(Ws, Test),
2354+
Res
2355+
end
2356+
end.
2357+
2358+
run_test_putenv(Env) ->
2359+
_ = [_ = os:putenv(Name, Value) || {Name,Value} <- Env],
2360+
ok.
2361+
2362+
run_test_unsetenv(Env) ->
2363+
_ = [_ = os:unsetenv(Name) || {Name,_Value} <- Env],
2364+
ok.
2365+
2366+
get_warnings([]) ->
2367+
[];
2368+
get_warnings(WsL) ->
2369+
case WsL of
2370+
[{_File,Ws}] -> {warnings, Ws};
2371+
_ -> {warnings, WsL}
2372+
end.
2373+
2374+
print_warnings(Warnings, Source) ->
2375+
Lines = binary:split(Source, <<"\n">>, [global]),
2376+
Cs = [print_warning(W, Lines) || W <- Warnings],
2377+
io:put_chars(Cs),
2378+
ok.
2379+
2380+
print_warning({{LineNum,Column},Mod,Data}, Lines) ->
2381+
Line0 = lists:nth(LineNum, Lines),
2382+
<<Line1:(Column-1)/binary,_/binary>> = Line0,
2383+
Spaces = re:replace(Line1, <<"[^\t]">>, <<" ">>, [global]),
2384+
CaretLine = [Spaces,"^"],
2385+
[io_lib:format("~p:~p: ~ts\n",
2386+
[LineNum,Column,Mod:format_error(Data)]),
2387+
Line0, "\n",
2388+
CaretLine, "\n\n"];
2389+
print_warning(_, _) ->
2390+
[].
2391+
2392+
fail() ->
2393+
ct:fail(failed).

lib/dialyzer/src/dialyzer_utils.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ sets_filter([Mod|Mods], ExpTypes) ->
767767

768768
src_compiler_opts() ->
769769
[no_copt, to_core, binary, return_errors,
770-
no_inline, strict_record_tests, strict_record_updates,
770+
no_inline, strict_record_tests,
771771
dialyzer, no_spawn_compiler_process].
772772

773773
-spec format_errors([{module(), string()}]) -> [string()].

0 commit comments

Comments
 (0)