├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── rebar.config ├── rebar.lock ├── src ├── check_syntax_spec.hrl ├── issues_spec.hrl ├── syntaxerl.app.src ├── syntaxerl.erl ├── syntaxerl_erl.erl ├── syntaxerl_escript.erl ├── syntaxerl_format.erl ├── syntaxerl_hrl.erl ├── syntaxerl_logger.erl ├── syntaxerl_script.erl ├── syntaxerl_terms.erl ├── syntaxerl_utils.erl ├── syntaxerl_xrl.erl └── syntaxerl_yrl.erl └── test ├── escript_error ├── escript_error.erl ├── escript_error.es ├── escript_error.escript ├── escript_no_main.escript ├── escript_ok ├── escript_ok.erl ├── escript_ok.es ├── escript_ok.escript ├── escript_warning ├── escript_warning.erl ├── escript_warning.es ├── escript_warning.escript ├── escript_with_module_ok.escript ├── hrl_error.hrl ├── hrl_ok.hrl ├── hrl_warning.hrl ├── projects ├── default │ ├── deps │ │ └── lib │ │ │ └── include │ │ │ └── lib.hrl │ ├── include │ │ └── ext.hrl │ └── src │ │ ├── int.hrl │ │ └── src.erl ├── rebar-apps │ ├── apps │ │ ├── app1 │ │ │ ├── include │ │ │ │ └── ext.hrl │ │ │ ├── rebar.config │ │ │ └── src │ │ │ │ ├── int.hrl │ │ │ │ └── src.erl │ │ └── app2 │ │ │ └── include │ │ │ └── app2.hrl │ ├── deps │ │ └── lib │ │ │ └── include │ │ │ └── lib.hrl │ └── rebar.config └── rebar3-apps │ ├── _build │ ├── default │ │ └── lib │ │ │ ├── lib1 │ │ │ └── include │ │ │ │ └── lib1.hrl │ │ │ └── lib2 │ │ │ └── src │ │ │ └── src.erl │ └── test │ │ └── lib │ │ └── lib2 │ │ └── include │ │ └── lib2.hrl │ ├── _checkouts │ └── lib3 │ │ └── include │ │ └── lib3.hrl │ ├── apps │ ├── app1 │ │ ├── include │ │ │ └── ext.hrl │ │ ├── rebar.config │ │ └── src │ │ │ ├── int.hrl │ │ │ ├── src.erl │ │ │ ├── subdir │ │ │ ├── inc.hrl │ │ │ └── subsrc.erl │ │ │ └── types.erl │ └── app2 │ │ └── include │ │ └── app2.hrl │ └── rebar.config ├── script_error.script ├── script_ok.script ├── terms_error.config ├── terms_ok.config └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | syntaxerl 3 | TAGS 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | sudo: false 3 | before_script: make 4 | script: 5 | - make dialyze 6 | - make test 7 | otp_release: 8 | - 17.5 9 | - 18.3 10 | - 19.3 11 | - 20.3 12 | - 21.3 13 | - 22.3 14 | - 23.3 15 | - 24.1 16 | notifications: 17 | email: 18 | - dm.klionsky@gmail.com 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Dmitry Klionsky aka ten0s 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: doc test 2 | 3 | REBAR3=rebar3 4 | 5 | all: escriptize 6 | 7 | escriptize: compile xref 8 | $(REBAR3) escriptize 9 | ln -fs _build/default/bin/syntaxerl syntaxerl 10 | 11 | compile: 12 | $(REBAR3) compile 13 | 14 | xref: compile 15 | $(REBAR3) xref 16 | 17 | dialyze: 18 | $(REBAR3) dialyzer 19 | 20 | clean: 21 | $(REBAR3) clean 22 | rm -f syntaxerl 23 | 24 | test: 25 | test/test.sh 26 | 27 | tags: 28 | find . -name "*.[e,h]rl" -print | etags - 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/ten0s/syntaxerl.svg?branch=master)](https://travis-ci.org/ten0s/syntaxerl) 2 | 3 | SyntaxErl 4 | ========= 5 | 6 | SyntaxErl is a syntax checker tool for Erlang. The syntax checker currently 7 | supports erlang source files (.erl), erlang header files (.hrl), erlang configs 8 | (.config, .rel, .script, .app, .app.src), escript files (.erl, .escript, .es), 9 | leex files (.xrl), and yecc files (.yrl). Its main purpose is to be used by tools 10 | like Emacs's [Flymake](https://github.com/ten0s/syntaxerl#flymake) and Vim's 11 | [Syntastic](https://github.com/ten0s/syntaxerl#syntastic) or 12 | [ALE](https://github.com/ten0s/syntaxerl#ale). 13 | 14 | SyntaxErl uses [rebar](https://github.com/rebar/rebar), 15 | [rebar3](https://github.com/erlang/rebar3) or [erlang.mk](https://erlang.mk) 16 | configs under the hood to determine deps and libs paths. Some common compile options 17 | are hardcoded, while others project specific are read from the config files. 18 | For the syntax checker to work correctly, make sure that your project is compilable 19 | and all the deps are at their places. 20 | 21 | Building 22 | -------- 23 | 24 | Information on building and installing [Erlang/OTP](http://www.erlang.org) 25 | can be found [here](https://github.com/erlang/otp/wiki/Installation) 26 | ([more info](https://github.com/erlang/otp/blob/master/INSTALL.md)). 27 | 28 | ### Dependencies 29 | 30 | To build SyntaxErl you will need a working installation of Erlang, git, and GNU make. 31 | 32 | #### Building SyntaxErl 33 | 34 | ```sh 35 | $ git clone git://github.com/ten0s/syntaxerl.git 36 | $ cd syntaxerl 37 | $ make 38 | ``` 39 | 40 | After performing the steps above in the current working directory you now 41 | have a script called `syntaxerl'. Place this script anywhere in your path. 42 | 43 | Usage 44 | ----- 45 | 46 | ### Command line 47 | 48 | ```sh 49 | $ syntaxerl 50 | Syntax checker for Erlang (0.16.0) 51 | Usage: syntaxerl [-d | --debug] 52 | syntaxerl <-h | --help> 53 | -d, --debug Enable debug output 54 | -h, --help Show this message 55 | ``` 56 | 57 | ### Emacs 58 | 59 | #### Flymake 60 | 61 | ```elisp 62 | ;;;---------------------------------------- 63 | ;;; erlang-mode 64 | ;;;---------------------------------------- 65 | 66 | (setq erlang-root-dir "/opt/r16b03-1") 67 | (setq load-path (cons (car (file-expand-wildcards (concat erlang-root-dir "/lib/tools-*/emacs"))) load-path)) 68 | (setq erlang-electric-commands nil) 69 | (require 'erlang-start) 70 | 71 | (add-hook 'erlang-mode-hook 72 | '(lambda() 73 | (imenu-add-to-menubar "Imenu"))) 74 | 75 | ; define auto erlang mode for these files/extensions. 76 | (add-to-list 'auto-mode-alist '(".*\\.app\\'" . erlang-mode)) 77 | (add-to-list 'auto-mode-alist '(".*app\\.src\\'" . erlang-mode)) 78 | (add-to-list 'auto-mode-alist '(".*\\.config\\'" . erlang-mode)) 79 | (add-to-list 'auto-mode-alist '(".*\\.rel\\'" . erlang-mode)) 80 | (add-to-list 'auto-mode-alist '(".*\\.script\\'" . erlang-mode)) 81 | (add-to-list 'auto-mode-alist '(".*\\.escript\\'" . erlang-mode)) 82 | (add-to-list 'auto-mode-alist '(".*\\.es\\'" . erlang-mode)) 83 | (add-to-list 'auto-mode-alist '(".*\\.xrl\\'" . erlang-mode)) 84 | (add-to-list 'auto-mode-alist '(".*\\.yrl\\'" . erlang-mode)) 85 | 86 | ; add include directory to default compile path. 87 | (defvar erlang-compile-extra-opts 88 | '(bin_opt_info debug_info (i . "../include") (i . "../deps") (i . "../../") (i . "../../../deps"))) 89 | 90 | ; define where put beam files. 91 | (setq erlang-compile-outdir "../ebin") 92 | 93 | ;;;---------------------------------------- 94 | ;;; flymake 95 | ;;;---------------------------------------- 96 | 97 | (require 'flymake) 98 | (require 'flymake-cursor) ; http://www.emacswiki.org/emacs/FlymakeCursor 99 | (setq flymake-log-level 3) 100 | 101 | (defun flymake-compile-script-path (path) 102 | (let* ((temp-file (flymake-init-create-temp-buffer-copy 103 | 'flymake-create-temp-inplace)) 104 | (local-file (file-relative-name 105 | temp-file 106 | (file-name-directory buffer-file-name)))) 107 | (list path (list local-file)))) 108 | 109 | (defun flymake-syntaxerl () 110 | (flymake-compile-script-path "~/bin/syntaxerl")) 111 | 112 | (add-hook 'erlang-mode-hook 113 | '(lambda() 114 | (add-to-list 'flymake-allowed-file-name-masks '("\\.erl\\'" flymake-syntaxerl)) 115 | (add-to-list 'flymake-allowed-file-name-masks '("\\.hrl\\'" flymake-syntaxerl)) 116 | (add-to-list 'flymake-allowed-file-name-masks '("\\.xrl\\'" flymake-syntaxerl)) 117 | (add-to-list 'flymake-allowed-file-name-masks '("\\.yrl\\'" flymake-syntaxerl)) 118 | (add-to-list 'flymake-allowed-file-name-masks '("\\.app\\'" flymake-syntaxerl)) 119 | (add-to-list 'flymake-allowed-file-name-masks '("\\.app.src\\'" flymake-syntaxerl)) 120 | (add-to-list 'flymake-allowed-file-name-masks '("\\.config\\'" flymake-syntaxerl)) 121 | (add-to-list 'flymake-allowed-file-name-masks '("\\.rel\\'" flymake-syntaxerl)) 122 | (add-to-list 'flymake-allowed-file-name-masks '("\\.script\\'" flymake-syntaxerl)) 123 | (add-to-list 'flymake-allowed-file-name-masks '("\\.escript\\'" flymake-syntaxerl)) 124 | (add-to-list 'flymake-allowed-file-name-masks '("\\.es\\'" flymake-syntaxerl)) 125 | 126 | ;; should be the last. 127 | (flymake-mode 1) 128 | )) 129 | 130 | ; see /usr/local/lib/erlang/lib/tools-/emacs/erlang-flymake.erl 131 | (defun erlang-flymake-only-on-save () 132 | "Trigger flymake only when the buffer is saved (disables syntax 133 | check on newline and when there are no changes)." 134 | (interactive) 135 | ;; There doesn't seem to be a way of disabling this; set to the 136 | ;; largest int available as a workaround (most-positive-fixnum 137 | ;; equates to 8.5 years on my machine, so it ought to be enough ;-) ) 138 | (setq flymake-no-changes-timeout most-positive-fixnum) 139 | (setq flymake-start-syntax-check-on-newline nil)) 140 | 141 | (erlang-flymake-only-on-save) 142 | ``` 143 | 144 | #### Erlang-flymake 145 | 146 | Your help is welcome. 147 | 148 | ### Vim 149 | 150 | #### Syntastic 151 | 152 | Setup everything as described [here](https://github.com/scrooloose/syntastic). 153 | Then add 154 | 155 | let g:syntastic_erlang_checkers=['syntaxerl'] 156 | 157 | to your vimrc. 158 | 159 | Thanks [locojay](https://github.com/locojay) for that. 160 | 161 | #### ALE 162 | 163 | Install [ALE](https://github.com/w0rp/ale) plugin for vim 8+ or neovim. 164 | 165 | To disable erlc linter please add the following lines to your vimrc: 166 | 167 | ``` 168 | let g:ale_linters = { 169 | \ 'erlang': ['syntaxerl'], 170 | \} 171 | ``` 172 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | warn_export_all, 3 | warnings_as_errors, 4 | warn_missing_spec 5 | ]}. 6 | 7 | {escript_name, "syntaxerl"}. 8 | 9 | {xref_warnings, true}. 10 | {xref_checks, [undefined_function_calls]}. 11 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /src/check_syntax_spec.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(check_syntax_spec_hrl). 2 | -define(check_syntax_spec_hrl, included). 3 | 4 | -include("issues_spec.hrl"). 5 | 6 | -spec check_syntax(FileName::file:filename(), BaseFileName::file:filename(), Debug::boolean()) -> 7 | {ok, [warning() | error()]} | {error, [error()]}. 8 | 9 | -spec output_error(ErrorInfo::error_info()) -> boolean(). 10 | 11 | -spec output_warning(ErrorInfo::error_info()) -> boolean(). 12 | 13 | -endif. 14 | -------------------------------------------------------------------------------- /src/issues_spec.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(issues_spec_hrl). 2 | -define(issues_spec_hrl, included). 3 | 4 | -type warning() :: {warning, Line::pos_integer(), Description::string()}. 5 | -type error() :: {error, Description::string()} | 6 | {error, Line::pos_integer(), Description::string()}. 7 | 8 | -type issue() :: warning() | error(). 9 | 10 | -type line() :: none | pos_integer(). 11 | 12 | -type error_info() :: { 13 | ErrorLine::line(), 14 | Module::module(), 15 | ErrorDescriptor::term() 16 | }. 17 | 18 | -type error_list() :: [{FileName::file:filename(), [ErrorInfo::error_info()]}]. 19 | -type warning_list() :: error_list(). 20 | 21 | -endif. 22 | -------------------------------------------------------------------------------- /src/syntaxerl.app.src: -------------------------------------------------------------------------------- 1 | {application, syntaxerl, [ 2 | {description, "Syntax checker for Erlang"}, 3 | {vsn, "0.16.0"}, 4 | {registered, []}, 5 | {applications, [ 6 | kernel, 7 | stdlib 8 | ]}, 9 | {env, []}, 10 | {handler_patterns, [ 11 | {[{suffix, ".hrl"}] , syntaxerl_hrl}, 12 | {[{suffix, ".escript"}] , syntaxerl_escript}, 13 | {[{suffix, ".es"}] , syntaxerl_escript}, 14 | {[{suffix, ".erl"}, shebang], syntaxerl_escript}, 15 | {[shebang] , syntaxerl_escript}, 16 | {[{suffix, ".erl"}] , syntaxerl_erl}, 17 | {[{suffix, ".xrl"}] , syntaxerl_xrl}, 18 | {[{suffix, ".yrl"}] , syntaxerl_yrl}, 19 | {[{suffix, ".script"}] , syntaxerl_script}, 20 | {[], syntaxerl_terms} 21 | ]} 22 | ]}. 23 | -------------------------------------------------------------------------------- /src/syntaxerl.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl). 2 | -author("Dmitry Klionsky "). 3 | -export([main/1]). 4 | 5 | -ignore_xref([ 6 | {behaviour_info, 1}, 7 | {main, 1} 8 | ]). 9 | 10 | -include("issues_spec.hrl"). 11 | -callback check_syntax(FileName::file:filename(), BaseFileName::file:filename(), Debug::boolean()) -> 12 | {ok, [warning() | error()]} | {error, [error()]}. 13 | -callback output_error(ErrorInfo::error_info()) -> boolean(). 14 | -callback output_warning(ErrorInfo::error_info()) -> boolean(). 15 | 16 | -define(EXIT_SUCCESS, 0). 17 | -define(EXIT_FAILURE, 1). 18 | -type exit_code() :: ?EXIT_SUCCESS | ?EXIT_FAILURE. 19 | 20 | %% =================================================================== 21 | %% API 22 | %% =================================================================== 23 | 24 | -record(opts, { 25 | filename :: file:filename() | undefined, 26 | base :: file:filename() | undefined, 27 | debug = false :: boolean() 28 | }). 29 | 30 | 31 | -spec main([string()]) -> no_return(). 32 | main(Args) -> 33 | Opts = parse_args(Args, #opts{}), 34 | #opts{filename = FileName, base = BaseFileName, debug = Debug} = Opts, 35 | check_syntax(FileName, BaseFileName, Debug). 36 | 37 | parse_args([], #opts{filename=undefined}) -> 38 | usage(?EXIT_FAILURE); 39 | parse_args([], Opts = #opts{base=undefined, filename=FileName}) -> 40 | Opts#opts{base=FileName}; 41 | parse_args([], Opts) -> 42 | Opts; 43 | parse_args([D | Args], Opts) when D =:= "-d"; D =:= "--debug" -> 44 | parse_args(Args, Opts#opts{debug=true}); 45 | parse_args([H | _Args], _Opts) when H =:= "-h"; H =:= "--help" -> 46 | usage(?EXIT_SUCCESS); 47 | parse_args([B | Args], Opts) when B =:= "-b"; B =:= "--base" -> 48 | case {Args, Opts} of 49 | {[BaseFileName | Args0], #opts{base=undefined}} -> 50 | parse_args(Args0, Opts#opts{base=BaseFileName}); 51 | {_Args, _Opts} -> 52 | usage(?EXIT_FAILURE) 53 | end; 54 | parse_args([FileName | Args], Opts = #opts{filename=undefined}) -> 55 | parse_args(Args, Opts#opts{filename=FileName}); 56 | parse_args(["--", FileName | Args], Opts = #opts{filename=undefined}) -> 57 | parse_args(Args, Opts#opts{filename=FileName}); 58 | parse_args(_Args, _Opts) -> 59 | usage(?EXIT_FAILURE). 60 | 61 | %% =================================================================== 62 | %% Internal 63 | %% =================================================================== 64 | 65 | -spec check_syntax(string(), string(), boolean()) -> exit_code(). 66 | check_syntax(FileName, BaseFileName, Debug) -> 67 | ScriptName = escript:script_name(), 68 | HandlerPatterns = handler_patterns(ScriptName), 69 | syntaxerl_logger:debug(Debug, "Handler patterns: ~p", [HandlerPatterns]), 70 | Handler = choose_handler(FileName, HandlerPatterns), 71 | syntaxerl_logger:debug(Debug, "Selected handler: ~p", [Handler]), 72 | case Handler:check_syntax(FileName, BaseFileName, Debug) of 73 | {ok, Issues} -> 74 | syntaxerl_utils:print_issues(FileName, Issues), 75 | halt(?EXIT_SUCCESS); 76 | {error, Issues} -> 77 | syntaxerl_utils:print_issues(FileName, Issues), 78 | halt(?EXIT_FAILURE) 79 | end. 80 | 81 | -spec usage(exit_code()) -> no_return(). 82 | usage(ExitCode) -> 83 | ScriptName = escript:script_name(), 84 | BaseName = filename:basename(ScriptName), 85 | case description_vsn(ScriptName) of 86 | {Description, Vsn} -> 87 | io:format("~s (~s)~n", [Description, Vsn]); 88 | _ -> 89 | io:format("~n") 90 | end, 91 | io:format("Usage: ~s [-b | --base ] [-d | --debug] ~n", [BaseName]), 92 | io:format(" ~s <-h | --help>~n", [BaseName]), 93 | io:format(" -b, --base Set original filename~n"), 94 | io:format(" -d, --debug Enable debug output~n"), 95 | io:format(" -h, --help Show this message~n"), 96 | halt(ExitCode). 97 | 98 | script_options(ScriptName) -> 99 | {ok, Sections} = escript:extract(ScriptName, []), 100 | Archive = proplists:get_value(archive, Sections), 101 | AppFile = lists:flatten(io_lib:format("~p/ebin/~p.app", [?MODULE, ?MODULE])), 102 | case zip:extract(Archive, [{file_list, [AppFile]}, memory]) of 103 | {ok, [{_, Binary}]} -> 104 | {ok, Tokens, _} = erl_scan:string(binary_to_list(Binary)), 105 | {ok, {application, ?MODULE, Options}} = erl_parse:parse_term(Tokens), 106 | Options; 107 | _ -> 108 | undefined 109 | end. 110 | 111 | description_vsn(ScriptName) -> 112 | case script_options(ScriptName) of 113 | undefined -> 114 | undefined; 115 | Options -> 116 | Description = proplists:get_value(description, Options), 117 | Vsn = proplists:get_value(vsn, Options), 118 | {Description, Vsn} 119 | end. 120 | 121 | handler_patterns(ScriptName) -> 122 | case script_options(ScriptName) of 123 | undefined -> 124 | undefined; 125 | Options -> 126 | proplists:get_value(handler_patterns, Options) 127 | end. 128 | 129 | choose_handler(FileName, [{Patterns, Handler} | HandlerPatterns]) -> 130 | case match_patterns(FileName, Patterns) of 131 | match -> 132 | Handler; 133 | nomatch -> 134 | choose_handler(FileName, HandlerPatterns) 135 | end; 136 | choose_handler(_FileName, []) -> 137 | erlang:error(no_handler_pattern_found). 138 | 139 | match_patterns(FileName, [{suffix, Suffix} | Patterns]) -> 140 | case lists:suffix(Suffix, FileName) of 141 | true -> 142 | match_patterns(FileName, Patterns); 143 | false -> 144 | nomatch 145 | end; 146 | match_patterns(FileName, [shebang | Patterns]) -> 147 | case read_first_byte(FileName) of 148 | {ok, <<"#">>} -> 149 | match_patterns(FileName, Patterns); 150 | _ -> 151 | nomatch 152 | end; 153 | match_patterns(_FileName, []) -> 154 | match. 155 | 156 | read_first_byte(FileName) -> 157 | case file:open(FileName, [read, binary]) of 158 | {ok, Fd} -> 159 | Result = 160 | case file:read(Fd, 1) of 161 | {ok, Byte} -> 162 | {ok, Byte}; 163 | _ -> 164 | error 165 | end, 166 | file:close(Fd), 167 | Result; 168 | _ -> 169 | error 170 | end. 171 | -------------------------------------------------------------------------------- /src/syntaxerl_erl.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_erl). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, BaseFileName, Debug) -> 19 | {InclDirs, DepsDirs, ErlcOpts} = syntaxerl_utils:incls_deps_opts(BaseFileName), 20 | syntaxerl_logger:debug(Debug, "Include dirs: ~p", [InclDirs]), 21 | syntaxerl_logger:debug(Debug, "Deps dirs: ~p", [DepsDirs]), 22 | syntaxerl_logger:debug(Debug, "Erlc opts: ~p", [ErlcOpts]), 23 | 24 | code:add_paths(DepsDirs), 25 | 26 | Result = compile:file(FileName, ErlcOpts ++ InclDirs), 27 | syntaxerl_logger:debug(Debug, "Compile result: ~p", [Result]), 28 | 29 | case Result of 30 | {ok, _ModuleName} -> 31 | {ok, []}; 32 | {ok, _ModuleName, Warnings} -> 33 | {ok, syntaxerl_format:format_warnings(?MODULE, Warnings)}; 34 | {error, Errors, Warnings} -> 35 | case syntaxerl_format:format_errors(?MODULE, Errors) of 36 | [] -> 37 | {ok, syntaxerl_format:format_warnings(?MODULE, Warnings)}; 38 | Errors2 -> 39 | {error, Errors2 ++ syntaxerl_format:format_warnings(?MODULE, Warnings)} 40 | end 41 | end. 42 | 43 | output_error(_) -> true. 44 | 45 | output_warning(_) -> true. 46 | -------------------------------------------------------------------------------- /src/syntaxerl_escript.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_escript). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, BaseFileName, Debug) -> 19 | case file:read_file(FileName) of 20 | {ok, Content} -> 21 | Preface = <<"-module(fixed_escript).\n-export([main/1]).\n">>, 22 | NewContent = 23 | case binary:split(Content, <<"\n">>) of 24 | [<<"#!", _/binary>>, Content0] -> 25 | <>; 26 | _Other -> <> 27 | end, 28 | NewFileName = filename:rootname(FileName, ".erl") ++ "_fixed.erl", 29 | case file:write_file(NewFileName, NewContent) of 30 | ok -> 31 | {InclDirs, DepsDirs, ErlcOpts} = syntaxerl_utils:incls_deps_opts(BaseFileName), 32 | syntaxerl_logger:debug(Debug, "Include dirs: ~p", [InclDirs]), 33 | syntaxerl_logger:debug(Debug, "Deps dirs: ~p", [DepsDirs]), 34 | syntaxerl_logger:debug(Debug, "Erlc opts: ~p", [ErlcOpts]), 35 | 36 | code:add_paths(DepsDirs), 37 | 38 | Result = compile:file(NewFileName, ErlcOpts ++ InclDirs), 39 | syntaxerl_logger:debug(Debug, "Compile result: ~p", [Result]), 40 | 41 | file:delete(NewFileName), 42 | 43 | case Result of 44 | {ok, _ModuleName} -> 45 | {ok, []}; 46 | {ok, _ModuleName, Warnings} -> 47 | io:format("Warnings: ~p~n", [Warnings]), 48 | {ok, syntaxerl_format:format_warnings( 49 | ?MODULE, fix_line_numbers(Warnings))}; 50 | {error, Errors, Warnings} -> 51 | case syntaxerl_format:format_errors( 52 | ?MODULE, fix_line_numbers(Errors)) of 53 | [] -> 54 | {ok, syntaxerl_format:format_warnings( 55 | ?MODULE, fix_line_numbers(Warnings))}; 56 | Errors2 -> 57 | {error, Errors2 ++ syntaxerl_format:format_warnings( 58 | ?MODULE, fix_line_numbers(Warnings))} 59 | end 60 | end; 61 | {error, Reason} -> 62 | {error, [{error, file:format_error(Reason)}]} 63 | end; 64 | {error, Reason} -> 65 | {error, [{error, file:format_error(Reason)}]} 66 | end. 67 | 68 | output_error(_) -> true. 69 | 70 | output_warning(_) -> true. 71 | 72 | %% =================================================================== 73 | %% Internal 74 | %% =================================================================== 75 | 76 | fix_line_numbers(ErrorList) -> 77 | ErrorList0 = skip_expected_errors(ErrorList), 78 | [{F, [{fix_line_number(L), M, E} || {L, M, E} <- Es]} 79 | || {F, Es} <- ErrorList0]. 80 | 81 | %% appropriately fix line numbers due to the `-module' definition. 82 | fix_line_number(none) -> 1; 83 | fix_line_number(Line) when Line < 3 -> 1; 84 | fix_line_number(Line) -> Line - 2. 85 | 86 | skip_expected_errors(ErrorList) -> 87 | [{F, lists:keydelete(redefine_module, 3, 88 | lists:keydelete({duplicated_export, {main, 1}}, 3, Es))} 89 | || {F, Es} <- ErrorList]. 90 | -------------------------------------------------------------------------------- /src/syntaxerl_format.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_format). 2 | -author("Dmitry Klionsky "). 3 | 4 | -export([ 5 | format_errors/2, 6 | format_warnings/2 7 | ]). 8 | 9 | -include("issues_spec.hrl"). 10 | 11 | %% =================================================================== 12 | %% API 13 | %% =================================================================== 14 | 15 | -spec format_errors(module(), error_list()) -> 16 | [{error, integer(), string()}]. 17 | format_errors(_Handler, []) -> 18 | []; 19 | format_errors(Handler, [{_FileName, Errors} | _]) -> 20 | [format_error(E) || E <- Errors, Handler:output_error(E)]. 21 | 22 | -spec format_warnings(module(), warning_list()) -> 23 | [{warning, integer(), string()}]. 24 | format_warnings(_Handler, []) -> 25 | []; 26 | format_warnings(Handler, [{_FileName, Warnings} | _]) -> 27 | [format_warning(W) || W <- Warnings, Handler:output_warning(W)]. 28 | 29 | %% =================================================================== 30 | %% Internal 31 | %% =================================================================== 32 | 33 | format_error({Line, _Module, _Term} = Error) -> 34 | Description = error_description(Error), 35 | FixedLine = fix_line_number(Line), 36 | {error, FixedLine, Description}. 37 | 38 | format_warning({Line, _Module, _Term} = Error) -> 39 | Description = error_description(Error), 40 | FixedLine = fix_line_number(Line), 41 | {warning, FixedLine, Description}. 42 | 43 | -spec error_description({integer(), module(), term()}) -> string(). 44 | error_description({_Line, Module, Error}) -> 45 | Module:format_error(Error). 46 | 47 | fix_line_number(none) -> 1; 48 | fix_line_number(Line) -> Line. 49 | -------------------------------------------------------------------------------- /src/syntaxerl_hrl.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_hrl). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, BaseFileName, Debug) -> 19 | case file:read_file(FileName) of 20 | {ok, Content} -> 21 | %% precede with the module name, so now this is a real erlang module. 22 | NewContent = << 23 | <<"-module(fixed_inc).\n">>/binary, 24 | Content/binary 25 | >>, 26 | %% append an erlang extention, so the `compile:file' won't complain. 27 | NewFileName = FileName ++ ".erl", 28 | case file:write_file(NewFileName, NewContent) of 29 | ok -> 30 | {InclDirs, DepsDirs, ErlcOpts} = syntaxerl_utils:incls_deps_opts(BaseFileName), 31 | syntaxerl_logger:debug(Debug, "Include dirs: ~p", [InclDirs]), 32 | syntaxerl_logger:debug(Debug, "Deps dirs: ~p", [DepsDirs]), 33 | syntaxerl_logger:debug(Debug, "Erlc opts: ~p", [ErlcOpts]), 34 | 35 | code:add_paths(DepsDirs), 36 | 37 | Result = compile:file(NewFileName, ErlcOpts ++ InclDirs), 38 | syntaxerl_logger:debug(Debug, "Compile result: ~p", [Result]), 39 | 40 | file:delete(NewFileName), 41 | 42 | case Result of 43 | {ok, _ModuleName} -> 44 | {ok, []}; 45 | {ok, _ModuleName, Warnings} -> 46 | {ok, syntaxerl_format:format_warnings( 47 | ?MODULE, fix_line_numbers(Warnings))}; 48 | {error, Errors, Warnings} -> 49 | case syntaxerl_format:format_errors( 50 | ?MODULE, fix_line_numbers(Errors)) of 51 | [] -> 52 | {ok, syntaxerl_format:format_warnings( 53 | ?MODULE, fix_line_numbers(Warnings))}; 54 | Errors2 -> 55 | {error, Errors2 ++ syntaxerl_format:format_warnings( 56 | ?MODULE, fix_line_numbers(Warnings))} 57 | end 58 | end; 59 | {error, Reason} -> 60 | {error, [{error, file:format_error(Reason)}]} 61 | end; 62 | {error, Reason} -> 63 | {error, [{error, file:format_error(Reason)}]} 64 | end. 65 | 66 | %% skip errors that might occur in pure header files. 67 | output_error({_, _, {spec_fun_undefined, _}}) -> false; 68 | output_error(_) -> true. 69 | 70 | %% skip warnings that might occur in pure header files. 71 | output_warning({_, _, {unused_record, _}}) -> false; 72 | output_warning({_, _, {unused_type, _}}) -> false; 73 | output_warning(_) -> true. 74 | 75 | %% =================================================================== 76 | %% Internal 77 | %% =================================================================== 78 | 79 | fix_line_numbers(ErrorList) -> 80 | [{F, [{fix_line_number(L), M, E} || {L, M, E} <- Es]} 81 | || {F, Es} <- ErrorList]. 82 | 83 | %% appropriately fix line numbers due to the `-module' definition. 84 | fix_line_number(none) -> 1; 85 | fix_line_number(Line) -> Line - 1. 86 | -------------------------------------------------------------------------------- /src/syntaxerl_logger.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_logger). 2 | -author("Dmitry Klionsky "). 3 | -export([debug/3]). 4 | 5 | %% =================================================================== 6 | %% API 7 | %% =================================================================== 8 | 9 | -spec debug(Output::boolean(), Fmt::string(), Args::[term()]) -> ok. 10 | debug(true, Fmt, Args) -> 11 | io:format("DEBUG: " ++ Fmt ++ "~n", Args); 12 | debug(false, _Fmt, _Args) -> 13 | ok. 14 | -------------------------------------------------------------------------------- /src/syntaxerl_script.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_script). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, _BaseFileName, _Debug) -> 19 | case syntaxerl_utils:consult_file(FileName) of 20 | {ok, _} -> 21 | {ok, []}; 22 | {error, Error} -> 23 | {error, [{error, file:format_error(Error)}]} 24 | end. 25 | 26 | output_error(_) -> true. 27 | 28 | output_warning(_) -> true. 29 | -------------------------------------------------------------------------------- /src/syntaxerl_terms.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_terms). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, _BaseFileName, _Debug) -> 19 | case file:open(FileName, [read]) of 20 | {ok, Fd} -> 21 | R = check_syntax(Fd), 22 | _ = file:close(Fd), 23 | R; 24 | {error, Error} -> 25 | {error, [format_error(Error)]} 26 | end. 27 | 28 | output_error(_) -> true. 29 | 30 | output_warning(_) -> true. 31 | 32 | %% =================================================================== 33 | %% Internal 34 | %% =================================================================== 35 | 36 | check_syntax(Fd) -> 37 | _ = epp:set_encoding(Fd), 38 | case find_errors(Fd, 1, []) of 39 | [] -> 40 | {ok, []}; 41 | Errors -> 42 | {error, [format_error(E) || E <- Errors]} 43 | end. 44 | 45 | find_errors(Fd, Line, Errors) -> 46 | case io:read(Fd, '', Line) of 47 | {ok, _, EndLine} -> 48 | find_errors(Fd, EndLine, Errors); 49 | {error, Error, EndLine} -> 50 | find_errors(Fd, EndLine, [Error | Errors]); 51 | {eof, _} -> 52 | lists:reverse(Errors) 53 | end. 54 | 55 | format_error(Error) -> 56 | {error, file:format_error(Error)}. 57 | -------------------------------------------------------------------------------- /src/syntaxerl_utils.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_utils). 2 | -author("Dmitry Klionsky "). 3 | 4 | -export([ 5 | incls_deps_opts/1, 6 | print_issues/2, 7 | consult_file/1 8 | ]). 9 | 10 | -include("issues_spec.hrl"). 11 | 12 | -define(REBAR3DEPSDIRS, [ 13 | "apps", 14 | "lib", 15 | "_checkouts", 16 | "_build/default/lib", 17 | "_build/test/lib" 18 | ]). 19 | 20 | %% =================================================================== 21 | %% API 22 | %% =================================================================== 23 | 24 | -spec incls_deps_opts(FileName::file:filename()) -> 25 | {InclDirs::[file:name()], EbinDirs::[file:name()], ErlcOpts::[term()]}. 26 | incls_deps_opts(FileName) -> 27 | AbsFileName = filename:absname(FileName), 28 | AppDir = appdir(AbsFileName), 29 | ProjectDir = projectdir(AppDir), 30 | StdOtpDirs = include_dirs(AbsFileName, AppDir, ProjectDir), 31 | StdErlcOpts = [ 32 | %% Since OTP 24 compiler warnings and errors include column numbers in 33 | %% addition to line numbers by default. We are not ready for this yet. 34 | %% The following option is ignored by older OTP releases. 35 | {error_location, line}, 36 | 37 | strong_validation, 38 | 39 | {warn_format, 1}, 40 | warn_export_vars, 41 | warn_shadow_vars, 42 | warn_obsolete_guard, 43 | warn_deprecated_function, 44 | warn_exported_vars, 45 | warn_bif_clash, 46 | warn_unused_function, 47 | warn_unused_variable, 48 | warn_unused_vars, 49 | warn_unused_record, 50 | 51 | return_errors, 52 | return_warnings 53 | ], 54 | Profile = which_compile_opts_profile(AbsFileName), 55 | {DepsDirs, ErlcOpts} = deps_opts(AppDir, StdOtpDirs, StdErlcOpts, Profile), 56 | 57 | ok = add_deps_paths(StdOtpDirs), 58 | 59 | {_, EbinDirs} = lists:mapfoldr(fun(Dir, Acc) -> {0, filelib:wildcard(Dir ++ "/*/ebin") ++ Acc} end, [], DepsDirs), 60 | IncDirs = lists:map(fun(Dir) -> {i, Dir} end, DepsDirs), 61 | {IncDirs, EbinDirs, ErlcOpts}. 62 | 63 | -spec appdir(AbsFileName::file:name()) -> file:name(). 64 | appdir(AbsFileName) -> 65 | appdir(AbsFileName, filename:dirname(AbsFileName)). 66 | 67 | -spec appdir(AbsFileName::file:name(), Default::file:name()) -> file:name(). 68 | appdir(AbsFileName, Default) -> 69 | AbsFileDir = filename:dirname(AbsFileName), 70 | case filename:basename(AbsFileDir) of 71 | Dir when Dir =:= "src"; Dir =:= "test"; Dir =:= "include" -> 72 | filename:dirname(AbsFileDir); 73 | Dir when Dir =/= "" -> 74 | appdir(AbsFileDir, Default); 75 | _Dir -> 76 | Default 77 | end. 78 | 79 | -spec projectdir(Dir::file:name()) -> file:name(). 80 | projectdir(Dir) -> 81 | PDir = filename:dirname(Dir), 82 | case filename:basename(PDir) of 83 | "apps" -> 84 | filename:dirname(PDir); 85 | "deps" -> 86 | filename:dirname(PDir); 87 | "lib" -> 88 | BuildDir = filename:dirname(filename:dirname(PDir)), 89 | case filename:basename(BuildDir) of 90 | "_build" -> filename:dirname(BuildDir); 91 | _ -> Dir 92 | end; 93 | _Dir -> 94 | Dir 95 | end. 96 | 97 | -spec include_dirs(AbsFileName, AppDir, ProjectDir) -> Dirs::[file:name()] 98 | when AbsFileName::file:name(), AppDir::file:name(), ProjectDir::file:name(). 99 | include_dirs(AbsFileName, AppDir, ProjectDir) -> 100 | SrcDir = filename:dirname(AbsFileName), 101 | IncludeDir = filename:join(AppDir, "include"), 102 | BuildDepsDir = filename:join(ProjectDir, "deps"), 103 | DepsDirs = [ filename:join(ProjectDir, Dir) || Dir <- ?REBAR3DEPSDIRS ], 104 | [SrcDir, IncludeDir, BuildDepsDir | DepsDirs]. 105 | 106 | -spec add_deps_paths(Dir::[file:name()]) -> ok. 107 | add_deps_paths(Dirs) -> 108 | lists:foreach(fun (Dir) -> 109 | case file:list_dir(Dir) of 110 | {ok, Deps} -> 111 | lists:foreach(fun (Dep) -> 112 | EBin = filename:join([Dir, Dep, "ebin"]), 113 | case filelib:is_dir(EBin) of 114 | true -> code:add_path(EBin); 115 | false -> ok 116 | end 117 | end, Deps); 118 | {error, _Error} -> 119 | ok 120 | end 121 | end, Dirs). 122 | 123 | -spec deps_opts(BaseDir::file:name(), OtpStdDirs::[file:name()], ErlcStdOpts::[term()], normal | test) -> {[file:name()], [term()]}. 124 | deps_opts(BaseDir, OtpStdDirs, ErlcStdOpts, Profile) -> 125 | {DepsDirs, ErlcOpts} = 126 | case find_deps_opts(BaseDir, Profile) of 127 | {ok, {RebarDepsDirs, RebarErlcOpts}} -> 128 | {RebarDepsDirs, RebarErlcOpts}; 129 | {error, bad_format} -> 130 | {[], []}; % nothing to do. fix your config file. :( 131 | {error, {erlangmk, _}} -> 132 | {[], []}; % Failure with make(1) or the Makefile. 133 | {error, not_found} -> 134 | % sinan, Emakefile, ... 135 | {[], []} 136 | end, 137 | UniqDepsDirs = uniq(OtpStdDirs ++ DepsDirs), 138 | UniqErlcOpts = uniq(ErlcStdOpts ++ ErlcOpts), 139 | {UniqDepsDirs, UniqErlcOpts}. 140 | 141 | -spec print_issues(FileName::file:filename(), Issues::[issue()]) -> ok. 142 | print_issues(_FileName, []) -> 143 | ok; 144 | print_issues(FileName, [Issue | Issues]) -> 145 | print_issue(FileName, Issue), 146 | print_issues(FileName, Issues). 147 | 148 | print_issue(FileName, {warning, Line, Description}) -> 149 | io:format("~s:~p: warning: ~s~n", [FileName, Line, Description]); 150 | print_issue(FileName, {error, Description}) -> 151 | io:format("~s:~s~n", [FileName, Description]); 152 | print_issue(FileName, {error, Line, Description}) -> 153 | io:format("~s:~p: ~s~n", [FileName, Line, Description]). 154 | 155 | -spec consult_file(file:filename()) -> {ok, term()} | {error, term()}. 156 | consult_file(File) -> 157 | case filename:extension(File) of 158 | ".script" -> 159 | consult_and_eval(remove_script_ext(File), File); 160 | _ -> 161 | Script = File ++ ".script", 162 | case filelib:is_regular(Script) of 163 | true -> 164 | consult_and_eval(File, Script); 165 | false -> 166 | file:consult(File) 167 | end 168 | end. 169 | 170 | 171 | %% =================================================================== 172 | %% API 173 | %% =================================================================== 174 | 175 | find_deps_opts(BaseDir, Profile) -> 176 | case which_build_tool(BaseDir) of 177 | {rebar, NewBaseDir} -> 178 | rebar_deps_opts(NewBaseDir); 179 | {erlangmk, NewBaseDir} -> 180 | erlangmk_deps_opts(NewBaseDir, Profile); 181 | undefined -> 182 | {error, not_found} 183 | end. 184 | 185 | which_compile_opts_profile(File) -> 186 | case filename:basename(filename:dirname(File)) of 187 | "test" -> test; 188 | _ -> normal 189 | end. 190 | 191 | which_build_tool("/") -> 192 | undefined; 193 | which_build_tool(BaseDir) -> 194 | RebarConfig = filename:join(BaseDir, "rebar.config"), 195 | case filelib:is_file(RebarConfig) of 196 | true -> 197 | {rebar, BaseDir}; 198 | false -> 199 | ErlangMk = filename:join(BaseDir, "erlang.mk"), 200 | case filelib:is_file(ErlangMk) of 201 | true -> 202 | {erlangmk, BaseDir}; 203 | false -> 204 | which_build_tool(filename:dirname(BaseDir)) 205 | end 206 | end. 207 | 208 | rebar_deps_opts("/") -> 209 | {error, not_found}; 210 | rebar_deps_opts(BaseDir) -> 211 | RebarConfig = filename:join(BaseDir, "rebar.config"), 212 | case filelib:is_file(RebarConfig) of 213 | true -> 214 | case consult_file(RebarConfig) of 215 | {ok, Terms} -> 216 | ErlcOpts = rebar_erl_opts(proplists:get_value(erl_opts, Terms, [])), 217 | 218 | RebarLibDirs = proplists:get_value(lib_dirs, Terms, []), 219 | RebarDepsDir = proplists:get_value(deps_dir, Terms, "deps"), 220 | LocalDirs = RebarLibDirs 221 | ++ [RebarDepsDir] 222 | ++ ?REBAR3DEPSDIRS 223 | ++ proplists:get_all_values(i, ErlcOpts), 224 | 225 | %% try to find recursively configs in parents directories. 226 | case rebar_deps_opts(filename:dirname(BaseDir)) of 227 | {ok, {ParentDirs, ParentErlcOpts}} -> 228 | {ok, {absdirs(BaseDir, uniq(LocalDirs ++ ParentDirs)), uniq(ErlcOpts ++ ParentErlcOpts)}}; 229 | {error, _} -> 230 | {ok, {absdirs(BaseDir, uniq(LocalDirs)), ErlcOpts}} 231 | end; 232 | {error, _} -> 233 | {error, bad_format} 234 | end; 235 | false -> 236 | rebar_deps_opts(filename:dirname(BaseDir)) 237 | end. 238 | 239 | -spec rebar_erl_opts(Opts) -> Opts when Opts :: [term()]. 240 | rebar_erl_opts(ErlOpts) -> 241 | rebar_erl_opts(ErlOpts, []). 242 | 243 | -spec rebar_erl_opts(Opts, Opts) -> Opts when Opts :: [term()]. 244 | rebar_erl_opts([], Acc) -> 245 | lists:reverse(Acc); 246 | rebar_erl_opts([{platform_define, ArchRegex, Key} | ErlOpts], Acc) -> 247 | rebar_erl_opts(ErlOpts, 248 | case is_arch(ArchRegex) of 249 | true -> [{d, Key} | Acc]; 250 | false -> Acc 251 | end); 252 | rebar_erl_opts([{platform_define, ArchRegex, Key, Value} | ErlOpts], Acc) -> 253 | rebar_erl_opts(ErlOpts, 254 | case is_arch(ArchRegex) of 255 | true -> [{d, Key, Value} | Acc]; 256 | false -> Acc 257 | end); 258 | rebar_erl_opts([ErlOpt | ErlOpts], Acc) -> 259 | rebar_erl_opts(ErlOpts, [ErlOpt | Acc]). 260 | 261 | -spec is_arch(ArchRegex :: string()) -> boolean(). 262 | is_arch(ArchRegex) -> 263 | re:run(get_arch(), ArchRegex, [{capture, none}]) =:= match. 264 | 265 | -spec get_arch() -> string(). 266 | get_arch() -> 267 | erlang:system_info(otp_release) ++ "-" 268 | ++ erlang:system_info(system_architecture) ++ "-" 269 | ++ wordsize(). 270 | 271 | -spec wordsize() -> string(). 272 | wordsize() -> 273 | try erlang:system_info({wordsize, external}) of 274 | Val -> 275 | integer_to_list(8 * Val) 276 | catch 277 | error:badarg -> 278 | integer_to_list(8 * erlang:system_info(wordsize)) 279 | end. 280 | 281 | erlangmk_deps_opts(BaseDir, Profile) -> 282 | Make = 283 | case os:getenv("MAKE") of 284 | false -> 285 | case os:find_executable("gmake") of 286 | false -> "make"; 287 | Path -> Path 288 | end; 289 | Cmd -> 290 | case (lists:member($/, Cmd) orelse lists:member($\\, Cmd)) of 291 | true -> Cmd; 292 | false -> os:find_executable(Cmd) 293 | end 294 | end, 295 | ERLC_OPTS_Target = 296 | case Profile of 297 | normal -> "show-ERLC_OPTS"; 298 | test -> "show-TEST_ERLC_OPTS" 299 | end, 300 | Args = [ 301 | "--no-print-directory", 302 | "-C", BaseDir, 303 | "show-ERL_LIBS", 304 | ERLC_OPTS_Target 305 | ], 306 | try 307 | Port = erlang:open_port({spawn_executable, Make}, [ 308 | {args, Args}, 309 | exit_status, use_stdio, stderr_to_stdout]), 310 | erlangmk_port_receive_loop(Port, "", BaseDir) 311 | catch 312 | error:_ -> 313 | {error, {erlangmk, make_execution_failure}} 314 | end. 315 | 316 | erlangmk_port_receive_loop(Port, Stdout, BaseDir) -> 317 | receive 318 | {Port, {exit_status, 0}} -> 319 | erlangmk_format_opts(Stdout, BaseDir); 320 | {Port, {exit_status, _}} -> 321 | {error, {erlangmk, make_target_failure}}; 322 | {Port, {data, Out}} -> 323 | erlangmk_port_receive_loop(Port, Stdout ++ Out, BaseDir) 324 | end. 325 | 326 | erlangmk_format_opts(Stdout, BaseDir) -> 327 | case string:tokens(Stdout, "\n") of 328 | [ErlLibsLine | ErlcOptsLines] -> 329 | ErlLibs = erlangmk_format_erl_libs(ErlLibsLine), 330 | ErlcOpts = erlangmk_format_erlc_opts(ErlcOptsLines, BaseDir), 331 | {ok, {ErlLibs, ErlcOpts}}; 332 | _ -> 333 | {error, {erlangmk, incorrect_output}} 334 | end. 335 | 336 | erlangmk_format_erl_libs(ErlLibsLine) -> 337 | case os:type() of 338 | {win32, _} -> string:tokens(ErlLibsLine, ";"); 339 | _ -> string:tokens(ErlLibsLine, ":") 340 | end. 341 | 342 | erlangmk_format_erlc_opts(ErlcOptsLines, BaseDir) -> 343 | erlangmk_format_erlc_opts(ErlcOptsLines, [], BaseDir). 344 | 345 | erlangmk_format_erlc_opts(["+" ++ Option | Rest], Opts, BaseDir) -> 346 | case make_term(Option) of 347 | {error, _} -> erlangmk_format_erlc_opts(Rest, Opts, BaseDir); 348 | Opt -> erlangmk_format_erlc_opts(Rest, [Opt | Opts], BaseDir) 349 | end; 350 | erlangmk_format_erlc_opts(["-I" ++ Opt | Rest], Opts, BaseDir) 351 | when Opt =/= "" -> 352 | erlangmk_format_erlc_opts(["-I", Opt | Rest], Opts, BaseDir); 353 | erlangmk_format_erlc_opts(["-I", [C | _] = Dir | Rest], Opts, BaseDir) 354 | when C =/= $- andalso C =/= $+ -> 355 | AbsDir = filename:absname(Dir, BaseDir), 356 | erlangmk_format_erlc_opts(Rest, [{i, AbsDir} | Opts], BaseDir); 357 | erlangmk_format_erlc_opts(["-W" ++ Warn | Rest], Opts, BaseDir) 358 | when Warn =/= "" -> 359 | erlangmk_format_erlc_opts(["-W", Warn | Rest], Opts, BaseDir); 360 | erlangmk_format_erlc_opts(["-W", Warn | Rest], Opts, BaseDir) -> 361 | case Warn of 362 | "all" -> 363 | erlangmk_format_erlc_opts(Rest, [{warn_format, 999} | Opts], 364 | BaseDir); 365 | "error" -> 366 | erlangmk_format_erlc_opts(Rest, [warnings_as_errors | Opts], 367 | BaseDir); 368 | "" -> 369 | erlangmk_format_erlc_opts(Rest, [{warn_format, 1} | Opts], 370 | BaseDir); 371 | _ -> 372 | try list_to_integer(Warn) of 373 | Level -> 374 | erlangmk_format_erlc_opts(Rest, 375 | [{warn_format, Level} | Opts], BaseDir) 376 | catch 377 | error:badarg -> 378 | erlangmk_format_erlc_opts(Rest, Opts, BaseDir) 379 | end 380 | end; 381 | erlangmk_format_erlc_opts(["-D" ++ Opt | Rest], Opts, BaseDir) 382 | when Opt =/= "" -> 383 | erlangmk_format_erlc_opts(["-D", Opt | Rest], Opts, BaseDir); 384 | erlangmk_format_erlc_opts(["-D", [C | _] = Val0 | Rest], Opts, BaseDir) 385 | when C =/= $- andalso C =/= $+ -> 386 | {Key0, Val1} = split_at_equals(Val0, []), 387 | Key = list_to_atom(Key0), 388 | case Val1 of 389 | [] -> 390 | erlangmk_format_erlc_opts(Rest, [{d, Key} | Opts], BaseDir); 391 | Val2 -> 392 | case make_term(Val2) of 393 | {error, _} -> 394 | erlangmk_format_erlc_opts(Rest, Opts, BaseDir); 395 | Val -> 396 | erlangmk_format_erlc_opts(Rest, [{d, Key, Val} | Opts], BaseDir) 397 | end 398 | end; 399 | erlangmk_format_erlc_opts([PathFlag, [_ | _] = Dir | Rest], Opts, BaseDir) 400 | when PathFlag =:= "-pa" orelse PathFlag =:= "-pz" -> 401 | AbsDir = filename:absname(Dir, BaseDir), 402 | case PathFlag of 403 | "-pa" -> code:add_patha(AbsDir); 404 | "-pz" -> code:add_pathz(AbsDir) 405 | end, 406 | erlangmk_format_erlc_opts(Rest, Opts, BaseDir); 407 | erlangmk_format_erlc_opts([_ | Rest], Opts, BaseDir) -> 408 | erlangmk_format_erlc_opts(Rest, Opts, BaseDir); 409 | erlangmk_format_erlc_opts([], Opts, _) -> 410 | lists:reverse(Opts). 411 | 412 | %% Function imported from erl_compile.erl from Erlang 19.1. 413 | make_term(Str) -> 414 | case erl_scan:string(Str) of 415 | {ok, Tokens, _} -> 416 | case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of 417 | {ok, Term} -> Term; 418 | {error, Reason} -> {error, Reason} 419 | end; 420 | {error, Reason, _} -> 421 | {error, Reason} 422 | end. 423 | 424 | %% Function imported from erl_compile.erl from Erlang 19.1. 425 | split_at_equals([$=|T], Acc) -> 426 | {lists:reverse(Acc),T}; 427 | split_at_equals([H|T], Acc) -> 428 | split_at_equals(T, [H|Acc]); 429 | split_at_equals([], Acc) -> 430 | {lists:reverse(Acc),[]}. 431 | 432 | consult_and_eval(File, Script) -> 433 | ConfigData = try_consult(File), 434 | file:script(Script, bs([{'CONFIG', ConfigData}, {'SCRIPT', Script}])). 435 | 436 | remove_script_ext(F) -> 437 | "tpircs." ++ Rev = lists:reverse(F), 438 | lists:reverse(Rev). 439 | 440 | try_consult(File) -> 441 | case file:consult(File) of 442 | {ok, Terms} -> 443 | Terms; 444 | {error, enoent} -> 445 | []; 446 | {error, Reason} -> 447 | syntaxerl_logger:debug(true, "Failed to read config file ~s: ~p~n", [File, Reason]) 448 | end. 449 | 450 | bs(Vars) -> 451 | lists:foldl(fun({K,V}, Bs) -> 452 | erl_eval:add_binding(K, V, Bs) 453 | end, erl_eval:new_bindings(), Vars). 454 | 455 | uniq(List) -> 456 | sets:to_list(sets:from_list(List)). 457 | 458 | absdirs(BaseDir, RelativeDirs) -> 459 | lists:map(fun(Dir) -> filename:join(BaseDir, Dir) end, RelativeDirs). 460 | -------------------------------------------------------------------------------- /src/syntaxerl_xrl.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_xrl). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, _BaseFileName, _Debug) -> 19 | case leex:file(FileName, [{report, true}, {return, true}]) of 20 | {ok, ScannerFile} -> 21 | file:delete(ScannerFile), 22 | {ok, []}; 23 | {ok, ScannerFile, Warnings} -> 24 | file:delete(ScannerFile), 25 | {ok, syntaxerl_format:format_warnings(?MODULE, Warnings)}; 26 | {error, Errors, Warnings} -> 27 | case syntaxerl_format:format_errors(?MODULE, Errors) of 28 | [] -> 29 | {ok, syntaxerl_format:format_warnings(?MODULE, Warnings)}; 30 | Errors2 -> 31 | {error, Errors2 ++ syntaxerl_format:format_warnings(?MODULE, Warnings)} 32 | end 33 | end. 34 | 35 | output_error(_) -> true. 36 | 37 | output_warning(_) -> true. 38 | -------------------------------------------------------------------------------- /src/syntaxerl_yrl.erl: -------------------------------------------------------------------------------- 1 | -module(syntaxerl_yrl). 2 | -author("Dmitry Klionsky "). 3 | 4 | -behaviour(syntaxerl). 5 | 6 | -export([ 7 | check_syntax/3, 8 | output_error/1, 9 | output_warning/1 10 | ]). 11 | 12 | -include("check_syntax_spec.hrl"). 13 | 14 | %% =================================================================== 15 | %% API 16 | %% =================================================================== 17 | 18 | check_syntax(FileName, _BaseFileName, _Debug) -> 19 | case yecc:file(FileName, [{report, true}, {return, true}]) of 20 | {ok, ScannerFile} -> 21 | file:delete(ScannerFile), 22 | {ok, []}; 23 | {ok, ScannerFile, Warnings} -> 24 | file:delete(ScannerFile), 25 | {ok, syntaxerl_format:format_warnings(?MODULE, Warnings)}; 26 | {error, Errors, Warnings} -> 27 | case syntaxerl_format:format_errors(?MODULE, Errors) of 28 | [] -> 29 | {ok, syntaxerl_format:format_warnings(?MODULE, Warnings)}; 30 | Errors2 -> 31 | {error, Errors2 ++ syntaxerl_format:format_warnings(?MODULE, Warnings)} 32 | end 33 | end. 34 | 35 | output_error(_) -> true. 36 | 37 | output_warning(_) -> true. 38 | -------------------------------------------------------------------------------- /test/escript_error: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg. 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_error.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg. 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_error.es: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg. 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_error.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg. 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_no_main.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -module(escript_no_main). 4 | 5 | main([]) -> 6 | ok. 7 | -------------------------------------------------------------------------------- /test/escript_ok: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | -spec main(list()) -> no_return(). 6 | main([]) -> 7 | no_arg; 8 | main([arg]) -> 9 | arg. 10 | -------------------------------------------------------------------------------- /test/escript_ok.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | -spec main(list()) -> no_return(). 6 | main([]) -> 7 | no_arg; 8 | main([arg]) -> 9 | arg. 10 | -------------------------------------------------------------------------------- /test/escript_ok.es: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | -spec main(list()) -> no_return(). 6 | main([]) -> 7 | no_arg; 8 | main([arg]) -> 9 | arg. 10 | -------------------------------------------------------------------------------- /test/escript_ok.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | -spec main(list()) -> no_return(). 6 | main([]) -> 7 | no_arg; 8 | main([arg]) -> 9 | arg. 10 | -------------------------------------------------------------------------------- /test/escript_warning: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg; 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_warning.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg; 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_warning.es: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg; 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_warning.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -export([main/1]). 4 | 5 | main([]) -> 6 | no_arg; 7 | main([arg]) -> 8 | arg. 9 | -------------------------------------------------------------------------------- /test/escript_with_module_ok.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | -module(escript_with_module_ok). 4 | 5 | -export([main/1]). 6 | 7 | -spec main(list()) -> no_return(). 8 | main([]) -> 9 | no_arg; 10 | main([arg]) -> 11 | arg. 12 | -------------------------------------------------------------------------------- /test/hrl_error.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(hrl_error_hrl). 2 | -define(hrl_error_hrl, included). 3 | 4 | -type test() :: {}}. 5 | 6 | -endif. 7 | -------------------------------------------------------------------------------- /test/hrl_ok.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(hrl_ok_hrl). 2 | -define(hrl_ok_hrl, included). 3 | 4 | -type test(_Value) :: {}. 5 | 6 | -endif. 7 | -------------------------------------------------------------------------------- /test/hrl_warning.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(hrl_warning_hrl). 2 | -define(hrl_warning_hrl, included). 3 | 4 | test() -> ok. 5 | 6 | -endif. 7 | -------------------------------------------------------------------------------- /test/projects/default/deps/lib/include/lib.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/default/include/ext.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/default/src/int.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/default/src/src.erl: -------------------------------------------------------------------------------- 1 | -module(src). 2 | 3 | -include_lib("lib/include/lib.hrl"). 4 | -include("ext.hrl"). 5 | -include("int.hrl"). 6 | -------------------------------------------------------------------------------- /test/projects/rebar-apps/apps/app1/include/ext.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar-apps/apps/app1/rebar.config: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ten0s/syntaxerl/b7d50eca703ba327b1cdab8162c480a30eb587b9/test/projects/rebar-apps/apps/app1/rebar.config -------------------------------------------------------------------------------- /test/projects/rebar-apps/apps/app1/src/int.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar-apps/apps/app1/src/src.erl: -------------------------------------------------------------------------------- 1 | -module(src). 2 | 3 | -include_lib("lib/include/lib.hrl"). 4 | -include_lib("app2/include/app2.hrl"). 5 | -include("ext.hrl"). 6 | -include("int.hrl"). 7 | -------------------------------------------------------------------------------- /test/projects/rebar-apps/apps/app2/include/app2.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar-apps/deps/lib/include/lib.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar-apps/rebar.config: -------------------------------------------------------------------------------- 1 | {lib_dirs, [ 2 | "apps" 3 | ]}. 4 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/_build/default/lib/lib1/include/lib1.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/_build/default/lib/lib2/src/src.erl: -------------------------------------------------------------------------------- 1 | -module(src). 2 | 3 | -include_lib("lib1/include/lib1.hrl"). % _build/default/lib 4 | -include_lib("lib2/include/lib2.hrl"). % _build/test/lib 5 | -include_lib("lib3/include/lib3.hrl"). % _checkouts 6 | -include_lib("app2/include/app2.hrl"). 7 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/_build/test/lib/lib2/include/lib2.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/_checkouts/lib3/include/lib3.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/include/ext.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/rebar.config: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ten0s/syntaxerl/b7d50eca703ba327b1cdab8162c480a30eb587b9/test/projects/rebar3-apps/apps/app1/rebar.config -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/src/int.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/src/src.erl: -------------------------------------------------------------------------------- 1 | -module(src). 2 | 3 | -include_lib("lib1/include/lib1.hrl"). % _build/default/lib 4 | -include_lib("lib2/include/lib2.hrl"). % _build/test/lib 5 | -include_lib("lib3/include/lib3.hrl"). % _checkouts 6 | -include_lib("app2/include/app2.hrl"). 7 | -include("ext.hrl"). 8 | -include("int.hrl"). 9 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/src/subdir/inc.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/src/subdir/subsrc.erl: -------------------------------------------------------------------------------- 1 | -module(subsrc). 2 | 3 | -include_lib("lib1/include/lib1.hrl"). % _build/default/lib 4 | -include_lib("lib2/include/lib2.hrl"). % _build/test/lib 5 | -include_lib("lib3/include/lib3.hrl"). % _checkouts 6 | -include_lib("app2/include/app2.hrl"). 7 | -include("ext.hrl"). 8 | -include("inc.hrl"). 9 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app1/src/types.erl: -------------------------------------------------------------------------------- 1 | -module(types). 2 | 3 | -export_type([mydict/0]). 4 | 5 | -ifdef(namespaced_types). 6 | -type mydict() :: dict:dict(). 7 | -else. 8 | -type mydict() :: dict(). 9 | -endif. 10 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/apps/app2/include/app2.hrl: -------------------------------------------------------------------------------- 1 | %% Included 2 | -------------------------------------------------------------------------------- /test/projects/rebar3-apps/rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [{platform_define, "^[0-9]+", namespaced_types}]}. 2 | -------------------------------------------------------------------------------- /test/script_error.script: -------------------------------------------------------------------------------- 1 | case os:getenv("REBAR_EXTRA_DEPS") of 2 | false -> 3 | CONFIG; 4 | _ -> 5 | case lists:keysearch(deps, 1, CONFIG) of 6 | {value, {deps, Deps}} -> 7 | NDeps = Deps ++ ExtraDeps, 8 | lists:keyreplace(deps, 1, CONFIG, {deps, NDeps}); 9 | false -> 10 | CONFIG ++ [{deps, ExtraDeps}] 11 | end 12 | end. 13 | -------------------------------------------------------------------------------- /test/script_ok.script: -------------------------------------------------------------------------------- 1 | ExtraDeps = [{retest, ".*", {git, "git://github.com/rebar/retest.git", 2 | {tag, "1.1.0"}}}], 3 | 4 | case os:getenv("REBAR_EXTRA_DEPS") of 5 | false -> 6 | CONFIG; 7 | _ -> 8 | case lists:keysearch(deps, 1, CONFIG) of 9 | {value, {deps, Deps}} -> 10 | NDeps = Deps ++ ExtraDeps, 11 | lists:keyreplace(deps, 1, CONFIG, {deps, NDeps}); 12 | false -> 13 | CONFIG ++ [{deps, ExtraDeps}] 14 | end 15 | end. 16 | -------------------------------------------------------------------------------- /test/terms_error.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | debug_info, 3 | fail_on_warning 4 | ]}. 5 | 6 | {xref_checks, [ 7 | undefined_function_calls 8 | ]} % missing period 9 | 10 | {cover_enabled, true}. 11 | {cover_print_enabled, true}. 12 | 13 | io:format("hello\n"). % bad term 14 | -------------------------------------------------------------------------------- /test/terms_ok.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | debug_info, 3 | fail_on_warning 4 | ]}. 5 | 6 | {xref_checks, [ 7 | undefined_function_calls 8 | ]}. 9 | 10 | {cover_enabled, true}. 11 | {cover_print_enabled, true}. 12 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR=$(dirname ${BASH_SOURCE[0]}) 4 | SYNTAXERL=${SCRIPT_DIR}/../syntaxerl 5 | 6 | EXIT=0 7 | 8 | function check() { 9 | local file="$1" 10 | local code="$3" 11 | local predicate="$4" 12 | local pattern="$5" 13 | 14 | echo -en "${file}\t" 15 | 16 | output=$(${SYNTAXERL} ${SCRIPT_DIR}/${file}) 17 | ret=$? 18 | echo -n code: ${code}/${ret} " " 19 | 20 | if [[ ${ret} == ${code} && ${predicate} == "w/" && ${pattern} == "" ]]; then 21 | echo -e "\e[32mOK\e[0m" 22 | elif [[ ${ret} == ${code} ]]; then 23 | echo ${output} | grep "${pattern}" > /dev/null 24 | ret2=$? 25 | echo -n grep: ${ret2} " " 26 | if [[ ${ret2} == 0 && "${predicate}" == "w/" ]]; then 27 | echo -e "\e[32mOK\e[0m" 28 | elif [[ ${ret2} == 1 && "${predicate}" == "w/o" ]]; then 29 | echo -e "\e[32mOK\e[0m" 30 | else 31 | echo -e "\e[31mFAIL\e[0m" 32 | EXIT=1 33 | fi 34 | else 35 | echo -e "\e[31mFAIL\e[0m" 36 | EXIT=1 37 | fi 38 | } 39 | 40 | check hrl_ok.hrl code 0 w/ "" 41 | check hrl_warning.hrl code 0 w/ "hrl_warning.hrl:4: warning: function test/0 is unused" 42 | check hrl_error.hrl code 1 w/ "hrl_error.hrl:4: syntax error before: '}'" 43 | 44 | check escript_ok code 0 w/ "" 45 | check escript_warning code 0 w/ "escript_warning:5: warning: missing specification for function main/1" 46 | check escript_error code 1 w/ "escript_error:7: function main/1 already defined" 47 | 48 | check escript_ok.erl code 0 w/ "" 49 | check escript_warning.erl code 0 w/ "escript_warning.erl:5: warning: missing specification for function main/1" 50 | check escript_error.erl code 1 w/ "escript_error.erl:7: function main/1 already defined" 51 | 52 | check escript_ok.es code 0 w/ "" 53 | check escript_warning.es code 0 w/ "escript_warning.es:5: warning: missing specification for function main/1" 54 | check escript_error.es code 1 w/ "escript_error.es:7: function main/1 already defined" 55 | 56 | check escript_ok.escript code 0 w/ "" 57 | check escript_warning.escript code 0 w/ "escript_warning.escript:5: warning: missing specification for function main/1" 58 | check escript_error.escript code 1 w/ "escript_error.escript:7: function main/1 already defined" 59 | 60 | check escript_with_module_ok.escript code 0 w/ "" 61 | 62 | check escript_no_main.escript code 0 w/ "" 63 | 64 | check script_ok.script code 0 w/ "" 65 | check script_error.script code 1 w/ "{unbound_var,'ExtraDeps'}" 66 | 67 | check terms_ok.config code 0 w/ "" 68 | check terms_error.config code 1 w/ "terms_error.config:10: syntax error before: '{'" 69 | check terms_error.config code 1 w/ "terms_error.config:13: bad term" 70 | 71 | check projects/default/src/src.erl code 0 w/ "" 72 | check projects/rebar-apps/apps/app1/src/src.erl code 0 w/ "" 73 | check projects/rebar3-apps/apps/app1/src/src.erl code 0 w/ "" 74 | check projects/rebar3-apps/apps/app1/src/subdir/subsrc.erl code 0 w/ "" 75 | check projects/rebar3-apps/_build/default/lib/lib2/src/src.erl code 0 w/ "" 76 | check projects/rebar3-apps/apps/app1/src/types.erl code 0 w/ "" 77 | 78 | exit ${EXIT} 79 | --------------------------------------------------------------------------------