Skip to content

Report undeclared names and code that is not strict Modelica 3.7 - #27

Merged
mdempse1 merged 7 commits into
mainfrom
pedantic-checks
Oct 25, 2026
Merged

mdempse1 merged 7 commits into
mainfrom
pedantic-checks

Conversation

@mdempse1

Copy link
Copy Markdown
Owner

Two opt-in checks for libraries that have to load in tools other than Dymola, a setting that lets a library accept the built-ins of the tools it is written for, and a resolver fix those checks exposed.

MLQT.Reference.Undeclared (Error when on)

Reports a name used where nothing of that name is declared:

  • variables in equations, algorithms, bindings, conditions and subscripts
  • functions called, types, base classes and imports
  • members after a dot (s.missing), enumeration literals included
  • modification targets (Sub s(gian = 1), and Real x(fixd = true) against the predefined type's attributes)
  • a non-constant of an enclosing class used from a nested one

It follows the roadmap's three-state rule. A miss is reported only when every scope searched is readable, with all bases loaded, and no unresolved import could bind the name. It also needs no unloaded library to be able to supply the name: either a plain variable, or a name the library's uses(...) rules out. Members reached through replaceable or an expandable connector are never judged.

Accepting a tool's built-ins. While the rule is on, the Static analysis section shows a Dymola and an OpenModelica checkbox, saved as AcceptedToolBuiltIns. The default, neither, reports every use and names the tool. Dymola covers its built-in functions, the Advanced/Hidden flags, Dymola_AST_* and its plot enumerations; OpenModelica covers its OpenModelica package. A tool's name counts even where an import binds part of it: import Modelica.Utilities.Strings.* brings in MSL's Strings.Advanced, and Dymola still reads Advanced.Translation.X as a flag. The CLI and the MCP style settings take the list too.

MLQT.Conformance.Grammar (Warning when on)

modelica.g4 accepts what Dymola accepts beyond the MLS 3.7 concrete syntax (Appendix A, keywords §2.3.3). This rule reports each place a class relies on one of the eight widenings:

  • a leading class annotation
  • a short-class prefix other than input/output
  • := in a modification
  • a der statement that is not der(x) := e
  • a comprehension after several arguments
  • an escape MLS does not define
  • white space in a quoted identifier
  • time or a predefined type's name used as an identifier

settings-reference.md §Strict Modelica 3.7 syntax lists them, and the places where the grammar is narrower than 3.7 (those are already syntax errors).

Complex is resolved, not predefined

TypeResolver.IsPredefined counted Complex as predefined, so every class built on it had no members: u.re resolved to nothing and the units on re/im were unseen. MLS 3.7 §4.9 does not list it; it is the operator record of Complex.mo. It now resolves like any class. Declaration ordering still treats a Complex declaration as a variable, so Format All moves nothing.

Also on this branch

  • Each rule's modifiers (the tool checkboxes, the package.order "match Dymola" switch) sit directly under that rule.
  • A scrollbar beside a splitter can be clicked: dividers grow their grab area into the second pane only (ResizablePanesJourney covers it).
  • Settings dialog layout improvements.

Measured

  • MSL 4.1.0, ModelicaTest, Buildings: neither new rule reports anything. 99% of MSL's ~200k name uses get a definite verdict, so the zero is a real result rather than silence.
  • Planted errors: a test library with eight planted mistakes, four of each kind, gets all eight reported.
  • A real application library (21,249 classes, Dymola accepted): no finding about a Dymola name. The rule's remaining findings there are protected members reached through a dot, and modifications naming elements that aren't there.
  • Resolution corpus: all 125,158 component types resolve (181 more than before, the Complex ones); no package redeclaration changes a unit.
  • Every rule on, before and after the Complex change: MSL and Buildings report the same findings, rule for rule, at the same speed.
  • Cost: the undeclared-name analysis is about 2 s over Buildings, and the conformance rule about 1 ms per class.

Test plan

  • CI=true dotnet build MLQT.slnx -c Release: no warnings
  • build/run-all-tests.ps1 -CoreOnly: 9,546 tests pass
  • build/check-coverage.ps1: gate passes
  • Resolution corpus (MLQT_RESOLUTION_CORPUS)
  • The Static analysis section's new checkboxes, by eye in the desktop app (covered by a bUnit test only)

🤖 Generated with Claude Code

mdempse1 and others added 7 commits October 25, 2026 12:56
Two opt-in rules for libraries that must load in tools other than Dymola.

MLQT.Reference.Undeclared (UndeclaredNameAnalyzer, Error when on) reports
each name used where nothing of that name is declared: variables in
equations, algorithms, bindings, conditions and subscripts; functions
called; types, bases and imports; members after a dot, enumeration
literals included; and the targets of modifications, checked against a
predefined type's attributes where the type is one. It follows the
roadmap's three-state rule: a miss is reported only when every scope the
lookup passed through is readable with all its bases loaded, no
unresolved import could bind the name, and no unloaded library could
supply it (a plain variable, or a name uses(...) rules out). Anything
reached through replaceable, expandable or Complex is never judged.
Measured: nothing reported over MSL 4.1.0, ModelicaTest and Buildings,
99% of MSL's 200k name uses judged, 4.6 s over Buildings.

MLQT.Conformance.Grammar (GrammarConformance, Warning when on) reports
each place a class parses only because modelica.g4 accepts more than MLS
3.7 Appendix A: a leading class annotation, a short-class prefix other
than input/output, ':=' in a modification, a der statement other than
der(x) := e, a comprehension after several arguments, escapes MLS does
not define, white space in a quoted identifier, and time or a predefined
type's name used as an identifier. settings-reference.md lists them, and
the places where the grammar is narrower than 3.7 instead.

firstTick and Subtask join ModelicaLanguage's built-in names (MLS §16).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MLQT.Reference.Undeclared reported every use of a tool's own functions
and flags, which is right for a library that must load anywhere and
noise for one written for a particular tool. AcceptedToolBuiltIns names
the tools whose built-ins count as declared; empty, the default, accepts
none, and the finding then says which tool the name belongs to.

ToolBuiltIns (ModelicaParser.Helpers) holds the names: Dymola's built-in
functions, its Advanced and Hidden flag packages and the Dymola_AST_
function family, matched by prefix as there is no complete list; and
OpenModelica's OpenModelica package. A tool's name is asked only after
lookup has found nothing, so a library's own class of that name still
wins, and a scope that is not complete still leaves it unjudged.

The Static analysis section shows a checkbox per tool while the rule is
on; the MCP style settings take the list, where only null leaves it
alone; and ChecksDifferFrom counts a change to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The accepted-tools checkboxes and the package.order "match Dymola"
switch each change one rule's findings, and sat below the whole list,
where nothing said which rule they belonged to. Each now follows its own
rule's row, chosen by the RuleIds constant rather than a literal id.

Also pins that Dymola's flags read and assigned in an algorithm count
as declared when Dymola is accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Dymola accepted, Advanced.Translation.X was still reported wherever
a class had import Modelica.Utilities.Strings.*: MSL has a
Strings.Advanced package, so lookup bound Advanced to it and failed on
Translation, and a tool's names were only offered where the first
segment resolved to nothing. Dymola reads its flags whatever an import
brings in, so a name that is the tool's whole is now the tool's however
far lookup got: declared where the tool is accepted, and said to be the
tool's where it is not. A name the libraries resolve completely still
means their class.

A listed Dymola name now also matches as the first segment, so the
literals of its plot enumerations (LinePattern.Solid, MarkerStyle.None)
are Dymola's too.

Measured over a large user repository of 21,249 classes with Dymola
accepted: no finding about a Dymola name is left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MudBlazor widens a divider's grab area with a ::before reaching 12px past
every side of the 4px gap. That lay over the first pane's scrollbar - the
tree's vertical one beside the tabs, the code viewer's horizontal one above
the findings - so pressing either started a drag and only the wheel scrolled.

Our dividers now grow their grab area into the second pane only, and sit
above it with z-index: 1. Without that, a positioned second pane (Code
Review's findings pane) paints over the ::before and the divider could be
grabbed by its gap alone.

ResizablePanesJourney checks both halves at every one of our dividers, and
fails against MudBlazor's own grab area.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TypeResolver's predefined list was the language's plus Complex, so every
class built on it - ComplexInput = input Complex, SI.ComplexVoltage =
Complex(...) - had no members as far as the resolver knew: u.re resolved
to nothing, and the units on re and im were never seen. MLS 3.7 §4.9
does not list Complex; it is the operator record of Complex.mo, a
library beside MSL, and now resolves as any class does. Without
Complex.mo loaded it is unresolved, as any library not loaded is.

UndeclaredNameAnalyzer no longer works round it, so members through
Complex are judged like any record's. CreateSimpleTypeLookup keeps
ordering a Complex declaration among the variables, as it always has,
so Format All rearranges nothing.

Measured: the resolution corpus resolves all 125,158 component types
(181 more than before, the Complex ones) with no redeclaration changing
a unit; MSL and Buildings with every rule on report the same findings,
rule for rule, at the same speed. Removing the workaround without this
fix gave 129 false findings on MSL's ComplexBlocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mdempse1
mdempse1 merged commit b414f37 into main Oct 25, 2026
14 checks passed
@mdempse1
mdempse1 deleted the pedantic-checks branch October 25, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant