argotorg / argotorg/solidity

Uppercase E exponent literal evades underscore validation check

Open
#16,740 0 comments 0 reactions 1 assignee Claimed by @blishko View on GitHub
bug :bug: low effort low impact
Dominant language
C++
Stars
25.7k
Forks
6.2k
Avg merge
1d 11h
Merged PRs (30d)
21

Description

## Description

Solidity rejects an underscore placed directly against the exponent marker of a decimal number literal, but only when the exponent marker is lowercase `e`. The post-lex validator in `libsolidity/analysis/SyntaxChecker.cpp:342-346` searches the literal text for the fixed *lowercase* substrings `"e_"` and `"_e"`:

```cpp
// libsolidity/analysis/SyntaxChecker.cpp:342-346 (inside visit(Literal), decimal branch)
if (value.find("_e") != ASTString::npos)
m_errorReporter.syntaxError(6415_error, _literal.location(),
"Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
if (value.find("e_") != ASTString::npos)
m_errorReporter.syntaxError(6165_error, _literal.location(),
"Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
```

The scanner equally accepts uppercase `E` as an exponent marker (`liblangutil/Scanner.cpp:996`, `m_char == 'e' || m_char == 'E'`), and the AST keeps the literal's original case, so `"1E_5"` contains `"E_"` and never matches `"e_"` — `find` returns `npos` and no error is emitted. As a result, every uppercase-`E` malformed literal slips through: `1E_5` and `1_E5` are silently accepted as `100000` (underscore dropped), while the byte-identical lowercase `1e_5`/`1_e5` are hard compile errors 6165/6415.

Expected: the underscore-placement check should be case-insensitive so that `1E_5`/`1_E5` are rejected with the same errors (6165/6415) as their lowercase equivalents, instead of silently accepting a malformed literal with no diagnostic.

## Environment

- Compiler version: `47b9dedda`

## Steps to Reproduce

The following compiles without any diagnostic; the underscores are silently dropped:

```solidity
uint constant A = 1E_5; // accepted, == 100000 (lowercase 1e_5 => Error 6165)
uint constant B = 1_E5; // accepted, == 100000 (lowercase 1_e5 => Error 6415)
uint constant C = 3E_0; // accepted, == 3
```

For comparison, the byte-identical lowercase forms `1e_5` and `1_e5` are hard errors (6165 and 6415 respectively), demonstrating the case-sensitivity asymmetry.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.