| 1 | ---
 | 
| 2 | in_progress: yes
 | 
| 3 | css_files: ../../web/base.css ../../web/manual.css ../../web/toc.css
 | 
| 4 | ---
 | 
| 5 | 
 | 
| 6 | List of Errors in the OSH Interpreter
 | 
| 7 | =====================================
 | 
| 8 | 
 | 
| 9 | <div id="toc">
 | 
| 10 | </div>
 | 
| 11 | 
 | 
| 12 | Parse Error:
 | 
| 13 |   Can be determined statically
 | 
| 14 |   spec/parse-errors.test.sh
 | 
| 15 | 
 | 
| 16 | 
 | 
| 17 | 
 | 
| 18 | TODO: See test/runtime-errors.sh.  Merge them here.
 | 
| 19 | 
 | 
| 20 | ## Syntax Errors in Oil String Literals
 | 
| 21 | 
 | 
| 22 | - `parse_backslash`
 | 
| 23 |   - no octal
 | 
| 24 |   - no `\z`
 | 
| 25 |   - no `\u{invalid`
 | 
| 26 | 
 | 
| 27 | - `parse_backticks`
 | 
| 28 |   - use "$(echo hi)"` and not backticks
 | 
| 29 | 
 | 
| 30 | ## Fatal vs. Non-Fatal
 | 
| 31 | 
 | 
| 32 | Fatal Error:
 | 
| 33 |   terminates the interpreter unconditionally, e.g. divide by zero does this in
 | 
| 34 |   bash.
 | 
| 35 | 
 | 
| 36 | Non-fatal error:
 | 
| 37 |   terminates the current builtin and exits 1
 | 
| 38 | 
 | 
| 39 | non-fatal errors can be turned into fatal errors.
 | 
| 40 | 
 | 
| 41 | by Strict modes:
 | 
| 42 |     set -o errexit
 | 
| 43 | 
 | 
| 44 | strict modes can also things that are not errors at all into fatal errors
 | 
| 45 |     set -o nounset
 | 
| 46 |     set -o failglob
 | 
| 47 | 
 | 
| 48 | Fatal errors can be turned into non-fatal ones!!!!
 | 
| 49 | 
 | 
| 50 | by dparen:
 | 
| 51 | 
 | 
| 52 |    (( 1 / 0 ))
 | 
| 53 | 
 | 
| 54 | by command sub -- although this involves another process so it's
 | 
| 55 | understandable!
 | 
| 56 | 
 | 
| 57 |    set -o errexit
 | 
| 58 |    echo $(exit 1)
 | 
| 59 | 
 | 
| 60 | ## Strict Modes
 | 
| 61 | 
 | 
| 62 | strict_array
 | 
| 63 | strict_errexit
 | 
| 64 | strict_arith
 | 
| 65 | 
 | 
| 66 | TODO: strict-word-eval?
 | 
| 67 |   for unicode errors
 | 
| 68 |   for subshell negative indices?  I think this is most consistent right now.
 | 
| 69 | 
 | 
| 70 | 
 | 
| 71 | ## Parse Error API
 | 
| 72 | 
 | 
| 73 | TODO:
 | 
| 74 | 
 | 
| 75 |     p_die() internally
 | 
| 76 | 
 | 
| 77 | 
 | 
| 78 |     w = w_parser.ReadWord()
 | 
| 79 |     if w is None:
 | 
| 80 |       do something with w_parser.Error()
 | 
| 81 | 
 | 
| 82 | Related to memory management API:
 | 
| 83 | 
 | 
| 84 |     # arena is the out param
 | 
| 85 |     arena = pool.NewArena()
 | 
| 86 |     c_parser = cmd_parse.CommandParser(w_parser, arena)
 | 
| 87 |     bool ok = c_parser.Parse()
 | 
| 88 |     if ok:
 | 
| 89 |       arena.RootNode() #  turns indexes into pointers?
 | 
| 90 |       arena.Deallocate()  # d
 | 
| 91 |     else:
 | 
| 92 |       c_parser.Error()  # Is this still a stack?
 | 
| 93 | 
 | 
| 94 | ## Runtime Error API: error codes + error contexts?
 | 
| 95 | 
 | 
| 96 | Idea:
 | 
| 97 | 
 | 
| 98 | - Should we have a table of errors for metaprogramming?
 | 
| 99 |   - assign each one of these a code, and decide what to do based on a table?
 | 
| 100 |   - then have an error CONTEXT
 | 
| 101 |   - based on spec tests?
 | 
| 102 | 
 | 
| 103 |   - and error context takes an error code, looks it up in a table, and decides
 | 
| 104 |     whether to catch or to reraise!
 | 
| 105 | 
 | 
| 106 | List of contexts:
 | 
| 107 | 
 | 
| 108 | - assignment   a=$()    exit code
 | 
| 109 | - command sub $()
 | 
| 110 | - subshell ()
 | 
| 111 | - pipeline?  ls | { foo; exit 1; }
 | 
| 112 | - dparen (( )) vs. arith sub $(( ))
 | 
| 113 | 
 | 
| 114 | ## Problem in bash: Context affects a lot
 | 
| 115 | 
 | 
| 116 | echo $(( 1 / 0 ))
 | 
| 117 | echo 'after-$(())
 | 
| 118 | (( 1 / 0 ))
 | 
| 119 | echo 'after-$(())
 | 
| 120 | 
 | 
| 121 | 
 | 
| 122 | ## Arith Eval
 | 
| 123 | 
 | 
| 124 | Divide by zero: $(( 1 / 0 ))
 | 
| 125 | 
 | 
| 126 |                       ^
 | 
| 127 | Maybe: integer overflow.  But we want big numbers.
 | 
| 128 | 
 | 
| 129 | Type errors between integers and strings:
 | 
| 130 | 
 | 
| 131 |     x=foo
 | 
| 132 |     $(( x * 2 ))  # doesn't make sense, except in bash's crazy world.
 | 
| 133 | 
 | 
| 134 | Invalid hex constant:
 | 
| 135 | 
 | 
| 136 |     x=0xabcg
 | 
| 137 |     echo $(( x * 2 ))   (fatal in bash)
 | 
| 138 | 
 | 
| 139 | ## Bool Eval
 | 
| 140 | 
 | 
| 141 | regcomp parse error: 
 | 
| 142 | 
 | 
| 143 | x=$(cat invalid-syntax.txt)
 | 
| 144 | [[ foo =~ $x ]]
 | 
| 145 | 
 | 
| 146 | ## Word Eval
 | 
| 147 | 
 | 
| 148 | IMPORTANT: Command sub error $(exit 1)
 | 
| 149 | 
 | 
| 150 | User-requested error:  ${undef?error}
 | 
| 151 | 
 | 
| 152 | set -o nounset
 | 
| 153 | 
 | 
| 154 |     def _EmptyStrOrError(self, val, token=None):
 | 
| 155 |       # calls `e_die()`
 | 
| 156 | 
 | 
| 157 | Variants:
 | 
| 158 |   nounset: index out of bounds ${a[3]}
 | 
| 159 |   I guess same diagnostic?
 | 
| 160 | 
 | 
| 161 | In bash you can set an index out of bounds, like
 | 
| 162 | b[2]=9  
 | 
| 163 | Might want to have a mode for this?
 | 
| 164 | 
 | 
| 165 | set -o failglob
 | 
| 166 |      TODO: not implemented
 | 
| 167 |      might need PWD diagnostic
 | 
| 168 |      
 | 
| 169 | 
 | 
| 170 | 
 | 
| 171 | Redirects:
 | 
| 172 |   Redirect to empty filename/descriptor ( or array)
 | 
| 173 | 
 | 
| 174 | { break; }   
 | 
| 175 |   ^~~~~~ break only invalid inside loop, etc.
 | 
| 176 | 
 | 
| 177 | 
 | 
| 178 | NotImplementedError
 | 
| 179 |   - e.g for var ref ${!a}
 | 
| 180 |   - bash associative arrays?  I think we want most of that
 | 
| 181 |   - $"" ?
 | 
| 182 |   - |& not yet done
 | 
| 183 |   - ;;& for case -- although parsing it is all of the work I guess
 | 
| 184 |   - some could be parse time errors too though?
 | 
| 185 | 
 | 
| 186 | 
 | 
| 187 | - String Slicing and String Length require valid utf-8 characters
 | 
| 188 | 
 | 
| 189 |     s=$(cat invalid.txt)
 | 
| 190 |     echo ${#s}  # code points
 | 
| 191 |     echo ${s:1:3}  # code points
 | 
| 192 | 
 | 
| 193 | - Slicing: Index is negative.  ${foo: -4} and ${foo: 1 : -4} aren't supported
 | 
| 194 |   right now, unlike bash and zsh.
 | 
| 195 | 
 | 
| 196 | ## Command Exec
 | 
| 197 | 
 | 
| 198 | IMPORTANT: subshell error ( exit 1 )
 | 
| 199 | 
 | 
| 200 | set -o errexit  -- turns NON-FATAL error into FATAL error.
 | 
| 201 | 
 | 
| 202 | set -o pipefail
 | 
| 203 |   pipefail might need some fanciness for ${PIPESTATUS}
 | 
| 204 | 
 | 
| 205 | Trying to set readonly variable:
 | 
| 206 |   readonly foo=bar
 | 
| 207 |   foo=x
 | 
| 208 |   (could any of this be done at compile time?)
 | 
| 209 | 
 | 
| 210 |   - this needs two locations: where the assignment was, and where it was
 | 
| 211 |     declared readonly.
 | 
| 212 | 
 | 
| 213 | Trying to redeclare a variable?  That can also be parse time.
 | 
| 214 | local x=1
 | 
| 215 | local x=2
 | 
| 216 | 
 | 
| 217 | Type errors between Str and StrArray:  -- strict-array controls this
 | 
| 218 |     EvalWordToString calls e_die()`
 | 
| 219 | 
 | 
| 220 |   echo foo > "$@"
 | 
| 221 |              ^--    # Should have what it evaluated to?  # This could be static too
 | 
| 222 | 
 | 
| 223 |   case "$@" in
 | 
| 224 |     "$@") echo bad;;
 | 
| 225 |   esac
 | 
| 226 | 
 | 
| 227 |   ${undef:-"$@"} is OK, but ${var%"$@"}  doesn't make sense really.
 | 
| 228 |   ${v/"$@"/"$@"}
 | 
| 229 | 
 | 
| 230 | 
 | 
| 231 | LHS evaluation:
 | 
| 232 |   s='abc'
 | 
| 233 |   s[1]=X  # invalid because it's a string, not an array
 | 
| 234 | 
 | 
| 235 | 
 | 
| 236 | Invalid descriptor:
 | 
| 237 | 
 | 
| 238 | 
 | 
| 239 | fd=$(cat invalid.txt)
 | 
| 240 | echo foo 2>& $fd
 | 
| 241 | 
 | 
| 242 | ### Builtins
 | 
| 243 | 
 | 
| 244 | In core/builtins.py:
 | 
| 245 | 
 | 
| 246 |     util.usage('...')
 | 
| 247 |     return 1
 | 
| 248 | 
 | 
| 249 | A usage error is a runtime error that results in the builtin returning 1.
 | 
| 250 | 
 | 
| 251 | Builtin has too many arguments -- but this falls under the errexit rule
 | 
| 252 |   cd foo bar baz
 | 
| 253 |   continue "$@"
 | 
| 254 | (Parse error: continue 1 2 3)
 | 
| 255 | 
 | 
| 256 | Although we might want to highlight the extra args.
 | 
| 257 | 
 | 
| 258 | 
 | 
| 259 | 
 | 
| 260 | ## Syscall Failures
 | 
| 261 | 
 | 
| 262 | Fatal error from system calls:
 | 
| 263 |     fork() could fail in theory
 | 
| 264 | 
 | 
| 265 | Some are not failures:
 | 
| 266 | 
 | 
| 267 |     stat() [[ -f /tmp/foo ]] 
 | 
| 268 |     cd /ff  chdir()  # exit code 1
 | 
| 269 |     cat <nonexistent  # This is just exit code 1 
 | 
| 270 | 
 | 
| 271 | ## Interpreter Failures
 | 
| 272 | 
 | 
| 273 | Runtime: Stack Too Deep (catch infinite recursion)
 | 
| 274 | Out of memory: should not happen with OSH, but maybe with Oil
 | 
| 275 | 
 | 
| 276 | Runtime Parse Errors
 | 
| 277 | --------------------
 | 
| 278 | 
 | 
| 279 | The way bash works 0x$var can be a hex literal.
 | 
| 280 | so var=xx makes this invalid.   hex/octal/decimal have this problem.
 | 
| 281 | 
 | 
| 282 | 
 | 
| 283 | Parse Time Errors
 | 
| 284 | -----------------
 | 
| 285 | 
 | 
| 286 | regcomp() errors (sometimes at parse time; other times at runtime)
 | 
| 287 | 
 | 
| 288 | Need to show stack trace for "source" like Python.  Prototype this.
 | 
| 289 | 
 | 
| 290 | Also might show which token thing caused you to be in arith parse state, like:
 | 
| 291 | 
 | 
| 292 | $((echo hi))
 | 
| 293 | ^~      ^~
 | 
| 294 | Arith   Invalid token
 | 
| 295 | 
 | 
| 296 | 
 |