Version 3.16.5 | Comprehensive Feature Reference
| Feature | Default | Access Method |
|---|---|---|
| Auto-save After Compile | OFF | Options |
| Build Statistics | OFF | DDevExtensions submenu |
| Code Quality Analyzer | ON | DDevExtensions submenu |
| Code Style Checker | ON | DDevExtensions submenu |
| Compile Backup | ON | Automatic |
| Compile Progress | ON | Automatic |
| Compile Style Check | OFF | Options (auto after compile) |
| Component Selector | OFF | Configurable hotkey |
| Confirm Ctrl+F1 While Debugging | ON | Automatic |
| Dead Code Detector | ON | DDevExtensions submenu |
| Decirculariser | ON | DDevExtensions submenu |
| DFM/PAS Consistency Checker | ON | DDevExtensions submenu |
| Dependency Viewer | OFF | DDevExtensions submenu |
| Disable Package Cache | OFF | Options |
| Disable Source Formatter Hotkey | OFF | Options |
| Don't Break on Spawned Processes | OFF | Options |
| Editor Tab Double-Click | Zoom | Double-click tab |
| Empty Event Handler Detector | ON | DDevExtensions submenu |
| Enhanced Key Bindings | ON | Automatic |
| External Mod Monitor | ON | Options (automatic monitoring) |
| File Cleaner | ON | Automatic |
| Find Unit Replacement | ON | Ctrl+Shift+A |
| Kill dexplore.exe on Exit | ON | Automatic |
| IDE Path Sorter | ON | Tools menu (below Build Tools...) |
| Sort Projects in Group | ON | Tools menu (below IDE Path Sorter) |
| IDE Path Compactor | ON | Tools menu (below IDE Path Sorter) |
| Method Separators | ON | Automatic (Options to configure) |
| Release Compiler Cache | OFF | Options |
| Remove Explicit* Properties | OFF | Options |
| Remove PixelsPerInch Property | OFF | Options |
| Remove TextHeight Property | OFF | Options |
| Replace Open File At Cursor | OFF | Options |
| Section Toggle (Interface/Implementation) | ON | Ctrl+Shift+Up/Down |
| Show All Inheritable Modules | OFF | Options |
| Show Project for Active File | ON | Automatic |
| Structure View Search | OFF | Configurable hotkey |
| Switch Project for File | ON | Automatic |
| TLabel.Margins.Bottom to Zero | ON | Automatic |
| TODO/FIXME Aggregator | ON | DDevExtensions submenu |
| Unreachable Code Detector | ON | DDevExtensions submenu |
| Unused Unit Detector | OFF | DDevExtensions submenu |
| Uses Clause Manager | ON | DDevExtensions submenu |
DDevExtensions v3.16.5 adds support for the Delphi 13.1 64-bit RAD Studio personality (bin64\bds.exe), which is where it was built and tested. RAD Studio 12.2 also ships a 64-bit IDE host, as an opt-in preview, and a DDevExtensionsD120x64.dll is built for it as of 2026-09-19 — but that build is untested, since no Delphi 12 installation was available. Delphi 10.2 – 11, and Delphi 12.0/12.1, have no 64-bit IDE host and continue to use the 32-bit DLL.
The 32-bit IDE host (bin\bds.exe) is unchanged and remains the primary target with the full feature set.
Features inactive on the 64-bit IDE host (rely on x86 inline asm or 5-byte JMP-rel32 hooks that don't port to Win64):
- Compile Progress hook
- Document Module handler
- Disable Alphasort Class Completion
- Show File Project In Project Manager
- Structure View Search filter
- All compile / file-notification subscribers (the IDE notifier is intentionally not registered on Win64 — a multi-interface dispatch ABI issue causes a deterministic AV in
rtl370.bplduring "Checking project dependencies..." otherwise) - Compile Interceptor input-handle redirect
- Chain-back callers of
RedirectOrgCall/CodeRedirect— hooks whose replacement calls the original function still need a trampoline that does not port to Win64, so they remain inactive.
Restored on Win64 in v3.20.9: the three Form Designer DFM-property cleaners — Remove Explicit*, Remove PixelsPerInch and Remove TextHeight — formerly fell under the
CodeRedirectrestriction above and were greyed out on the 64-bit host. They are full-replacement hooks (their replacementDefinePropertiesnever calls the original), so they now install through an x64-safe primitive (InstallFullReplaceHook, a 14-byte absolute indirect jump) and are fully functional on the 64-bit IDE. The globalCodeRedirectis unchanged, so the genuine chain-back hooks listed above stay inactive.
All other features — Dependency Viewer, Code Quality Analyzer, Unused Unit Detector, Dead Code Detector, Unreachable Code Detector, Empty Event Handler Detector, Uses Clause Manager, Decirculariser, DFM/PAS Consistency Checker, Code Style Checker, TODO/FIXME Aggregator, IDE Path Sorter, Sort Projects in Group, Build Statistics, External Mod Monitor, Method Separators, etc. — run on the 64-bit host.
Win64 shutdown diagnostic log. If anything in the plug-in raises an exception while the 64-bit IDE is shutting down, a single attributed line is appended to:
%APPDATA%\DDevExtensions\Win64Shutdown.log
A clean shutdown leaves the file unchanged. If you ever see new lines appear after exiting the 64-bit IDE, send the line — it identifies the exact step that faulted (e.g. step TCompileProgress.FormNativeProgress.Free threw EAccessViolation: ...). Live capture is also possible by running DebugView or DebugView++ elevated with Capture Win32 + Capture Global Win32 enabled. The 32-bit IDE does not produce this log.
- Open RAD Studio/Delphi IDE
- Go to Tools > DDevExtensions > Options...
- In the Options dialog, look for DDevExtensions in the left tree
- Expand to see all feature categories
All DDevExtensions tools are organized under a single submenu, ordered by recommended workflow:
Tools
└── DDevExtensions
├── Options...
├── ─────────────
├── Dependency Viewer... (1. Architecture overview)
├── Code Quality Analyzer... (2. Comprehensive quality check)
├── Unused Unit Detector... (3. Remove unused units)
├── Dead Code Detector... (4. Remove dead code)
├── Unreachable Code Detector...(5. Remove unreachable code)
├── Empty Event Handler... (6. Remove empty handlers)
├── Uses Clause Manager... (7. Organize uses clauses)
├── Decirculariser... (8. Break circular references)
├── DFM/PAS Consistency... (9. Check form/code sync)
├── Code Style Checker... (10. Style check)
└── TODO/FIXME Aggregator... (11. Review work items)
├── IDE Path Sorter... (IDE path management - separate from DDevExtensions submenu)
├── IDE Path Compactor... (Shorten the IDE library path strings)
└── Sort Projects in Group... (Alphabetise the open project group's members)
DDevExtensions_Map.html - Interactive visual map showing:
- All components and their relationships
- Lines of code per component
- Architecture overview (Core, Analysis Tools, Shared Libraries, etc.)
- Build dependencies and installation process
- Click any component for detailed description
Open DDevExtensions_Map.html in a browser to explore the project structure.
DDevExtensions stores settings in:
%APPDATA%\DDevExtensions\
Each feature has its own XML configuration file (e.g., KeyBindings.xml, CompileProgress.xml).
While all features are independent and can be enabled/disabled individually, the following sequence provides a logical workflow for getting the most out of DDevExtensions.
Start with these core enhancements that improve everyday IDE interaction:
| Feature | Why First |
|---|---|
| DSUFeatures | Extended IDE settings provide the base configuration |
| Editor Enhancements | Core editing improvements you'll use constantly |
| Enhanced Key Bindings | Keyboard shortcuts for efficient navigation |
Once the foundation is set, enable features that speed up navigation and component work:
| Feature | Purpose |
|---|---|
| UnitSelector | Find/Use unit dialogs with fuzzy matching |
| ComponentSelector | Quick component search in form designer |
| StrucViewSearch | Filter Structure View to find methods quickly |
| Section Toggle | Jump between interface/implementation sections |
Add features that improve the compile and project workflow:
| Feature | Purpose |
|---|---|
| CompileProgress | Visual build progress and taskbar integration |
| Build Statistics | Track compile times and code metrics |
| CompileBackup | Pre-compile backup for safety |
| FileCleaner | Automatic cleanup of unnecessary files |
| StartParameterTeam | Shared start parameters across team |
These tools analyze existing code patterns and dependencies. They're most valuable once your project structure is established. The menu is ordered by recommended workflow - work through them top to bottom for a comprehensive code review:
| Step | Feature | Purpose |
|---|---|---|
| 1 | Dependency Viewer | Start here - understand architecture, find circular references, check layer violations |
| 2 | Code Quality Analyzer | Comprehensive scan for magic numbers, hardcoded strings, empty excepts, etc. |
| 3 | Unused Unit Detector | Remove units that are no longer needed |
| 4 | Dead Code Detector | Find procedures, functions, and fields that are never used |
| 5 | Unreachable Code Detector | Find code within functions that can never execute |
| 6 | Empty Event Handler Detector | Find event handlers that were created but never implemented |
| 7 | Uses Clause Manager | After cleanup, organize remaining uses clauses |
| 8 | Decirculariser | Rank the circular unit references and break the dangerous ones |
| 9 | DFM/PAS Consistency | Verify form files match their code files |
| 10 | Code Style Checker | Check naming conventions and style rules |
| 11 | TODO/FIXME Aggregator | Review outstanding work items and technical debt |
For Code Review / Cleanup:
- Run tools 1-6 to identify and remove unnecessary code
- Run tools 7-8 to organize what remains
- Run tools 9-10 to polish and document
For New Projects: Start with Groups 1-3 enabled for daily development
Before Release: Run all analysis tools (1-10) for a comprehensive cleanup pass
Maintenance: Periodically run Dependency Viewer to catch architectural drift
- All features can be disabled via environment variable:
DDevExtensions.DisabledFeatures(semicolon-separated list) - Each feature stores settings independently in
%APPDATA%\DDevExtensions\ - No feature depends on another being enabled - use what works for your workflow
Purpose: Automatically saves all modified editor files after a successful compilation.
Default: OFF
Location: Options > DDevExtensions > Compile Progress
Purpose: Tracks compilation time for each unit, displays code metrics, and optionally shows style violations.
Default: OFF
Location: Options > DDevExtensions > Compile Progress
Access: Tools > DDevExtensions > Build Statistics...
Build Stats Tab:
- Per-unit compile time in milliseconds
- Lines of Code (LOC) per unit
- Cyclomatic Complexity per unit
- Sortable columns
- Filter: All / Project / External files
- Export to CSV
- Copy to clipboard
- Double-click to open unit (if source available)
Style Issues Tab (New in 3.7.0):
- Shows all Code Style Checker violations from the last compile
- Requires "Run style check after compile" option enabled
- Sortable columns: Unit, Category, Rule, Line, Expected, Actual, Severity
- Filter by Category: All / Naming Convention / Anti-Pattern
- Double-click to open file at violation line
- Export to CSV
- Copy to clipboard
- Right-click context menu for all actions
- Go to Options > DDevExtensions > Compile Progress
- Check "Enable Build Statistics"
- Optionally check "Show after compile" for automatic display
- New in 3.7.0: Check "Run style check after compile" to populate the Style Issues tab
| Column | Description |
|---|---|
| Unit Name | The unit being compiled |
| Duration | Compile time in ms, seconds, or minutes |
| LOC | Lines of Code (non-blank, non-comment) |
| Complexity | Cyclomatic complexity score |
| File Path | Full or relative path to source |
Cyclomatic Complexity measures the number of independent paths through code:
- 1-10: Simple, low risk
- 11-20: Moderate complexity
- 21-50: High complexity, consider refactoring
- 50+: Very high risk, should be simplified
| Column | Description |
|---|---|
| Unit | Source file containing the violation |
| Category | "NamingConvention" or "AntiPattern" |
| Rule | Which style rule was violated |
| Line | Line number in source |
| Expected | What was expected (e.g., prefix, max depth) |
| Actual | What was found |
| Severity | Warning or Error |
Build Stats Tab:
- All: Shows all units compiled (project + external)
- Project: Only units that are part of your project
- External: RTL/VCL and third-party units
Style Issues Tab:
- All: Shows all style violations
- Naming Convention: Only naming rule violations (T, I, F, E, P, A prefixes)
- Anti-Pattern: Only anti-pattern violations (empty finally, nested with, etc.)
Note: Metrics (LOC, Complexity) only display when source code is available. External units without source show "-".
Purpose: A unified tool to detect common code quality issues including magic numbers, hardcoded strings, commented-out code, exception handling problems, and memory leak patterns.
Default: ON
Location: Options > DDevExtensions > Code Quality Analyzer
Access: Tools > DDevExtensions > Code Quality Analyzer...
Settings File: %APPDATA%\DDevExtensions\CodeQualityAnalyzer.xml
| Category | Description | Severity |
|---|---|---|
| Magic Number | Hardcoded numeric literals that should be constants | Warning |
| Hardcoded String | String literals that should be resource strings or constants | Info |
| Commented Code | Blocks of commented-out code (not documentation) | Info |
| Empty Except | Exception handlers with no code inside | Warning |
| Catch-All Exception | Generic except without on E: clause |
Warning |
| Missing Try/Finally | Object creation not followed by try/finally/Free pattern | Warning |
| Memory Leak | Potential memory leak where object may not be freed | Warning |
Magic Numbers Tab:
- Check for magic numbers: Enable/disable magic number detection
- Whitelist: Comma-separated list of allowed numbers (e.g.,
0,1,-1,2,10,100,1000) - Allow in array indices: Skip magic numbers used as array indices (e.g.,
arr[0],arr[1])
Strings Tab:
- Check for hardcoded strings: Enable/disable string literal detection
- Minimum length: Only flag strings longer than this value (default: 3)
- Exclude format strings: Skip strings containing
%s,%d, etc. - Exclude SQL keywords: Skip strings containing SQL keywords like SELECT, INSERT, UPDATE
Comments Tab:
- Check for commented-out code: Enable/disable commented code detection
- Detection threshold: Minimum score to consider a comment as containing code (default: 3)
Exceptions Tab:
- Check for empty except blocks: Flag
except...endwith no statements - Check for catch-all handlers: Flag
exceptwithouton E:clause
Memory Tab:
- Check for Create without try/finally: Flag object creation not protected by try/finally
- Check for potential memory leaks: Flag objects that may not be freed
- Ignore patterns: Comma-separated patterns to ignore (e.g.,
TStringList,TMemoryStream)
- Open your project in the Delphi IDE
- Go to Tools > DDevExtensions > Code Quality Analyzer...
- Click Check Project to scan all Pascal files
- Results appear in the list with Unit, Line, Category, Severity, and Description
- Double-click any issue to navigate to the source code location
- Use the Category and Severity filters to focus on specific issue types
- Click Export CSV... to save results for review
| Column | Description |
|---|---|
| Unit | Name of the unit containing the issue |
| Line | Line number where the issue was found |
| Category | Type of issue (Magic Number, Hardcoded String, etc.) |
| Severity | Info, Warning, or Error |
| Description | Detailed description of the issue |
| File | Full path to the source file |
- Start with defaults: The default whitelist and thresholds work well for most projects
- Tune the whitelist: Add project-specific constants to the magic number whitelist
- Focus on high severity: Address Warning-level issues before Info-level ones
- Use filters: When reviewing a large codebase, filter by category to focus on one issue type at a time
- Export for team review: Export results to CSV for code review meetings
Important: Not every finding requires action. The analyzer identifies potential issues for your review - use your judgment to decide what to address.
Severity Levels:
- Error = Critical issue that should be fixed (severe memory leak patterns, dangerous exception handling)
- Warning = Likely a real issue that should be addressed (magic numbers, empty except blocks, memory patterns)
- Info = Suggestions that may or may not apply to your situation (hardcoded strings, commented code)
Hardcoded Strings - What's Usually OK:
- Database field names (
'JobNo','CustomerID','DateCreated') - these rarely change - SQL table/column names - tied to database schema
- Debug/logging messages (
'Starting process...','End of method') - Internal identifiers and keys
- Format strings containing
%s,%d(excluded by default)
Hardcoded Strings - Consider Changing:
- User-facing messages and labels - especially if localization is planned
- Error messages shown to users
- Strings duplicated in multiple places (indicates need for a constant)
Magic Numbers - Usually Worth Fixing:
- Timeout values (e.g.,
180000→const TIMEOUT_MS = 180000) - Status codes (e.g.,
200,404→HTTP_OK,HTTP_NOT_FOUND) - Business logic constants (e.g.,
60→SECONDS_PER_MINUTE) - Array sizes and limits (e.g.,
100→MAX_ITEMS)
Magic Numbers - Often OK:
- Loop counters with 0/1 (whitelisted by default)
- Simple arithmetic with small numbers
- Values in const declarations (analyzer excludes these)
Memory/Exception Patterns:
- These findings are more likely to indicate real bugs
- Empty except blocks almost always indicate swallowed errors
- Missing try/finally around Create calls can cause memory leaks
- Review these carefully, even if false positives exist
Reducing Noise:
- Adjust the magic number whitelist for your project's common values
- Increase minimum string length if too many short strings are flagged
- Use the Severity filter to focus on Warnings first
- Disable specific check categories that don't apply to your project
Purpose: Checks code against Delphi naming conventions, custom variable prefix rules, and common anti-patterns.
Default: ON
Location: Options > DDevExtensions > Code Style Checker
Access: Tools > DDevExtensions > Code Style Checker...
Settings File: %APPDATA%\DDevExtensions\CodeStyleChecker.xml
| Rule | Convention | Example |
|---|---|---|
| Types | Prefix with T | TMyClass |
| Interfaces | Prefix with I | IMyInterface |
| Fields | Prefix with F | FMyField |
| Exceptions | Prefix with E | EMyError |
| Pointers | Prefix with P | PMyRecord |
| Parameters | Prefix with A | AValue (optional) |
The Code Style Checker now detects common structural anti-patterns in your code:
| Anti-Pattern | Description | Default |
|---|---|---|
| Empty Finally | finally blocks with no statements |
Enabled |
| Nested With | with statements nested more than 1 level deep |
Enabled |
| Deep Nesting | Control flow nesting exceeding threshold | Enabled (4 levels) |
| Long Method | Methods exceeding line count threshold | Enabled (100 lines) |
| Long Parameter List | Methods with too many parameters | Enabled (6 params) |
Why These Are Anti-Patterns:
Empty Finally Blocks:
try
DoSomething;
finally
// Empty - no cleanup code!
end;An empty finally block suggests forgotten cleanup code or unnecessary try/finally structure.
Nested With Statements:
with Customer do
with Orders do // Nested with - ambiguous scope
with Items do // Even deeper - very confusing
ShowMessage(Name); // Which Name? Customer? Order? Item?Nested with statements make code hard to read and maintain. The identifier scope becomes ambiguous.
Deep Nesting:
if Condition1 then
if Condition2 then
for I := 0 to Count do
if Condition3 then
try
if Condition4 then // 5 levels deep - hard to followDeeply nested code is hard to read, test, and maintain. Consider extracting methods or using early exits.
Long Methods: Methods exceeding 100 lines (configurable) are difficult to understand and test. Consider breaking into smaller, focused methods.
Long Parameter Lists:
procedure ProcessOrder(Customer: TCustomer; Order: TOrder;
Items: TItems; Discount: Currency; Tax: Currency;
Shipping: Currency; Notes: string); // 7 parametersToo many parameters suggest the method is doing too much, or parameters should be grouped into a record/object.
| Option | Description | Default |
|---|---|---|
| Check Anti-Patterns | Master switch for all anti-pattern detection | ON |
| Check Empty Finally | Detect empty finally blocks | ON |
| Check Nested With | Detect nested with statements | ON |
| Check Deep Nesting | Detect excessive control flow nesting | ON |
| Check Long Methods | Detect methods exceeding line threshold | ON |
| Check Long Param Lists | Detect methods with too many parameters | ON |
| Max Nesting Depth | Threshold for deep nesting (1-10) | 4 |
| Max Method Lines | Threshold for long methods | 100 |
| Max Parameters | Threshold for parameter count | 6 |
This is an additional check, separate from the standard Delphi naming rules above. It validates variable names in var sections against custom type-to-prefix mappings.
Important: These are optional style suggestions, not requirements. Your code will compile and run correctly regardless of these warnings. Variable prefix conventions (sometimes called Hungarian notation) are a matter of personal or team preference - many modern style guides do not require them. You can:
- Ignore the warnings if you prefer descriptive names without prefixes
- Disable this check entirely in Options → Code Style Checker
- Customize the prefix rules to match your team's conventions
How the checkbox affects results:
| Checkbox State | What Gets Checked |
|---|---|
| Unchecked | Only standard Delphi rules (T, I, F, E, P, A prefixes) |
| Checked | Standard Delphi rules PLUS variable type prefix rules |
Example: With rules String=s and Boolean=l configured:
var
CustomerName: String; // Flagged - should be sCustomerName
IsActive: Boolean; // Flagged - should be lIsActive
MyForm: TMyForm; // NOT checked (no rule for TMyForm)Configuration: Click "Edit Rules..." to open the popup editor.
Format: One rule per line as Type=Prefix
Example Rules:
String=s
Integer=i
Boolean=l
Real=r
Double=f
Single=f
Variant=v
Char=c
Currency=r
array of String=sa
array of Integer=na
array of Double=na
array of Byte=na
Features:
- Supports simple types (String, Integer, Boolean, etc.)
- Supports
array of Xsyntax (e.g.,array of String=sa) - Same prefix can be used for related types (e.g.,
ffor both Double and Single) - Only checks variables whose types have a matching rule defined
- Types without rules are ignored (not flagged)
- Settings persist between IDE sessions
- Conflict detection: Warns when rule patterns could clash (see below)
Note: The default rules are preset with the developer's preferences as examples. Users should click "Reset to Defaults" or edit to match their own coding standards.
Type prefix rules use prefix matching - a pattern like TList will match types like TList, TListBox, TListView, etc. This means rule order matters: the first matching rule wins.
Conflict Warning in Editor:
When editing rules, the editor displays a warning if one pattern could match types intended for another more specific rule:
Warning: Rule order matters. "TList -> TStringList" - first pattern may match types intended for second.
Solution: Put more specific rules before general ones:
TStringList=sl ← More specific (check first)
TObjectList=ol
TList=l ← Less specific (check last)
Conflict Indication in Results:
When a type matches a less specific rule but a more specific rule also exists, the violation includes a note.
Example: If you have these rules:
TList=l
TStringList=sl
And code like:
var
MyList: TStringList;The listing would show:
| Unit | Rule | Line | Expected | Actual | Severity |
|---|---|---|---|---|---|
| MyUnit | VariablePrefix | 42 | l... (for TList) [Note: also matches TStringList rule] | MyList | Warning |
The key indicator is [Note: also matches TStringList rule] appended to the Expected column.
This alerts you that:
TStringListmatched theTListrule first (becauseTListis a prefix ofTStringList)- There's a more specific
TStringListrule that was bypassed - You should reorder your rules (put
TStringListbeforeTList)
After reordering rules:
TStringList=sl ← Check first (more specific)
TList=l ← Check second (less specific)
The listing would then show:
| Unit | Rule | Line | Expected | Actual | Severity |
|---|---|---|---|---|---|
| MyUnit | VariablePrefix | 42 | sl... (for TStringList) | MyList | Warning |
No conflict note - the correct rule matched.
Note: Simple types like Boolean, Integer, Double, String, Currency etc. never conflict because none is a prefix of another. Conflicts only occur with class-type rules where one pattern starts with another (e.g., TList and TStringList).
Checks that uses clauses use fully qualified unit names with scope prefixes.
| Unqualified (flagged) | Qualified (correct) |
|---|---|
SysUtils |
System.SysUtils |
Classes |
System.Classes |
Forms |
Vcl.Forms |
Windows |
Winapi.Windows |
DB |
Data.DB |
Covers 97 common RTL, VCL, Winapi, Data, and XML units.
Naming Convention Rules (enable/disable individually):
- Check Types: T prefix for types
- Check Interfaces: I prefix for interfaces
- Check Fields: F prefix for fields
- Check Exceptions: E prefix for exceptions
- Check Pointers: P prefix for pointer types
- Check Parameters: A prefix for parameters (off by default)
Variable Type Prefix Rules:
- Check variable prefixes by type: Master switch for type-based variable prefix checking
- Edit Rules...: Opens popup editor for type-to-prefix mappings
Uses Clause Rules:
- Check unit scope names: Flags uses clauses missing scope prefixes (e.g., System., Vcl.)
| Column | Description |
|---|---|
| Unit | Source file name |
| Category | "NamingConvention" or "AntiPattern" |
| Rule | Which rule was violated |
| Line | Line number |
| Expected | What was expected (prefix for naming, threshold for anti-patterns) |
| Actual | The identifier or value that violated the rule |
| Severity | Warning or Error |
The results can be filtered by category:
- All: Shows all violations
- Naming Convention: Only prefix naming rule violations (T, I, F, E, P, A)
- Anti-Pattern: Only anti-pattern violations (EmptyFinally, NestedWith, DeepNesting, LongMethod, LongParamList)
Purpose: Creates backup copies of files before compilation.
Default: ON
Location: Options > DDevExtensions > Compile Backup
Purpose: Adds a progress bar to the compile dialog, shows compilation progress in the Windows taskbar, and optionally runs code style checking after compilation.
Default: ON
Location: Options > DDevExtensions > Compile Progress
| Option | Description | Default |
|---|---|---|
| Enable Build Statistics | Track compile times and code metrics | OFF |
| Show after compile | Auto-show Build Statistics dialog | OFF |
| Run style check after compile | Run Code Style Checker after successful compile | OFF |
When "Run style check after compile" is enabled:
- After a successful compilation, the Code Style Checker automatically runs on all project units
- Any style violations (naming conventions and anti-patterns) are collected
- Results are stored and displayed in the Style Issues tab of the Build Statistics dialog
- The style check runs silently - no popup unless you open Build Statistics
Benefits:
- Continuous code quality monitoring without manual scans
- Catch style issues as you develop
- Review all issues in one place after each build
- Export violation reports for code review
Note: The style check only runs after successful compiles. Failed compilations skip the style check.
Purpose: Provides a searchable popup dialog for quickly finding and selecting components.
Default: OFF (no hotkey assigned)
Location: Options > DDevExtensions > Component Selector
- Enable and assign a hotkey (e.g., Ctrl+Shift+C)
- In the form designer, press the hotkey
- Type to search for a component
- Press Enter to select and place the component
Purpose: Shows a confirmation dialog before opening help during debug sessions.
Default: ON
Location: Options > DDevExtensions > Extended IDE Settings
Why: Prevents accidentally interrupting debugging to view help documentation.
Purpose: Finds procedures, functions, and fields that are never referenced.
Default: ON
Location: Options > DDevExtensions > Dead Code Detector
Access: Tools > DDevExtensions > Dead Code Detector...
- Unused procedures and functions
- Unused private fields
- Unused protected fields
- Virtual/override methods (may be called polymorphically)
- Abstract methods
- Constructors and destructors
- Event handlers (OnClick, etc.)
- Published members (used by RTTI)
- Interface implementations
- Enabled: Master switch
- Check Procedures: Detect unused procedures/functions
- Check Fields: Detect unused fields
- Ignore List: Patterns to skip (supports wildcards)
Use wildcards to ignore common patterns:
*Click
*Change
*Execute
Get*
Set*
| Column | Description |
|---|---|
| Unit | Source file name |
| Type | Procedure, Function, or Field |
| Name | The unused element |
| Scope | Private, Protected, Public |
| Line | Line number |
Purpose: Detects inconsistencies between DFM files and their corresponding PAS file field declarations.
Default: ON
Location: Options > DDevExtensions > DFM/PAS Consistency
Access: Tools > DDevExtensions > DFM/PAS Consistency...
When you rename or delete components in the form designer, sometimes the DFM and PAS files can get out of sync:
- A component is deleted from the DFM but the field declaration remains in the PAS (Missing in DFM)
- A component exists in the DFM but no field declaration in PAS (Missing in PAS)
- A field is renamed in the PAS but not updated in the DFM
- A component type is changed but the field type doesn't match (Type Mismatch)
These inconsistencies can cause runtime errors, access violations, or unexpected behavior.
| Issue Type | Description |
|---|---|
| Missing in PAS | Component exists in DFM but field is not declared in the form class |
| Missing in DFM | Field declared in PAS but no corresponding component in DFM (orphaned declaration) |
| Type Mismatch | Component exists in both but types differ |
Note: "Missing in DFM" detection intelligently filters out false positives:
- Collection types: TStringList, TList, TDictionary, TStack, TQueue
- Graphics objects: TBitmap, TIcon, TPicture, TBrush, TPen, TFont
- Stream types: TStream and descendants
- Thread types: TThread and descendants
- Utility types: TIniFile, TRegistry, JSON types, XML types
- State/activity types: TGridDrawState, TCloseAction, TShiftState, etc.
- Abstract base classes: TDataSet, TField, TComponent, TControl, TWinControl
- Method parameters: Correctly skips procedure/function parameter declarations
- Open the tool via Tools > DDevExtensions > DFM/PAS Consistency...
- Click "Scan Project" to analyze all forms in the project
- Review the results showing any inconsistencies
- Double-click an item to navigate:
- Missing in PAS: Opens the form designer and selects the component
- Missing in DFM: Opens the PAS file at the field declaration line
- Type Mismatch: Opens the PAS file at the field declaration line
- Fix the issues manually in either the DFM or PAS file
| Column | Description |
|---|---|
| Unit | The form/data module name |
| Component | Name of the component/field |
| Issue | Type of inconsistency (Missing in PAS, Missing in DFM, Type Mismatch) |
| PAS Type | Field type as declared in the PAS file (empty for Missing in PAS) |
| PAS Line | Line number in the PAS file where the field is declared ("-" if not in PAS) |
| DFM Type | Component type as declared in the DFM file (empty for Missing in DFM) |
| DFM Line | Line number in the DFM file where the component is defined ("-" if not in DFM) |
| File | Path to the DFM file |
Note: The PAS Line and DFM Line columns are centered for easier reading.
Important: Not all "Missing in PAS" findings indicate a problem. Components can be categorized as:
Input Controls (Review Required): These components typically need PAS declarations because code usually reads/writes their values:
- TEdit, TMemo, TRichEdit, TMaskEdit
- TComboBox, TListBox, TCheckBox, TRadioButton
- TDateTimePicker, TSpinEdit, TTrackBar
- Third-party input controls (TLMDLabeledEdit, TcxTextEdit, etc.)
If an input control is missing from PAS, investigate how the code accesses its value - it may use FindComponent(), data binding, or it could be a bug.
Passive Controls (Usually Safe to Ignore): These components often don't need PAS declarations as they're purely visual:
- TLabel, TStaticText - Display static text
- TPanel, TGroupBox, TScrollBox - Layout containers
- TBevel, TShape, TImage, TSplitter - Decorative/structural
- TToolBar, TStatusBar, TMainMenu - UI framework
Note: Event handlers still work without PAS declarations. A button's OnClick will fire even without a Button1: TButton field - the event is wired in the DFM. However, you cannot reference Button1.Caption in code without the declaration.
"Missing in DFM" means a field is declared in the PAS file but there's no corresponding component in the DFM. This typically indicates:
Orphaned Declarations (Should Fix):
- A component was deleted from the form but the field declaration wasn't removed
- A component was renamed in the form but the old declaration remains
- Copy/paste errors left stale declarations
What to Do:
- Double-click to navigate to the declaration in the PAS file
- Verify the component doesn't exist in the form
- Delete the orphaned field declaration from the class
Note: The detector automatically filters out common non-component types (TStringList, TBitmap, TThread, etc.) that are legitimately declared as fields without being DFM components.
Use the Filter dropdown to focus on specific component types:
- All: Show all inconsistencies
- Input Controls: Show only input controls (TEdit, TComboBox, etc.) - items that likely need attention
- Passive Controls: Show only passive controls (TLabel, TPanel, etc.) - items usually safe to ignore
Missing in PAS:
Unit: Form1
Component: Button1
Issue: Missing in PAS
PAS Type: (empty)
DFM Type: TButton
The DFM contains object Button1: TButton but the form class doesn't declare a Button1 field.
Missing in DFM:
Unit: Form1
Component: edtUserName1
Issue: Missing in DFM
PAS Type: TRzDBEdit
DFM Type: (empty)
The PAS file declares edtUserName1: TRzDBEdit but there's no corresponding component in the DFM. This is likely an orphaned declaration from a deleted component.
Type Mismatch:
Unit: Form1
Component: Panel1
Issue: Type Mismatch
PAS Type: TGroupBox
DFM Type: TPanel
The DFM has object Panel1: TPanel but the PAS declares Panel1: TGroupBox.
- Non-modal: The dialog stays open while you work in the IDE, allowing you to double-click items to navigate and fix issues without closing the checker
- Clears on close: Results are cleared when the dialog is closed; re-scan when you open it again
- Singleton: Clicking the menu item while the dialog is open brings it to front
- Enabled: Master switch for the feature
Purpose: Finds event handlers that have empty bodies (just begin/end with no code).
Default: ON
Location: Options > DDevExtensions > Empty Event Handler
Access: Tools > DDevExtensions > Empty Event Handler Detector...
Empty event handlers are often created accidentally when double-clicking components in the form designer. They:
- Clutter your code with unnecessary methods
- Increase the compiled executable size slightly
- Make code harder to read and maintain
- May indicate forgotten implementation
Event handlers with empty bodies - methods that only contain:
procedure TForm1.Button1Click(Sender: TObject);
begin
// Nothing here - just begin/end
end;The detector recognizes common event handler naming patterns:
*Click- Button clicks, menu clicks*Change- Edit changes, combobox changes*Create- Form/frame creation*Destroy- Form/frame destruction*Enter,*Exit- Focus events*KeyDown,*KeyUp,*KeyPress- Keyboard events*MouseDown,*MouseUp,*MouseMove- Mouse events- And many more...
- Open the tool via Tools > DDevExtensions > Empty Event Handler Detector...
- Click "Scan Project" to analyze all units in the project
- Review the results showing empty event handlers
- Double-click an item to navigate to the source
- Delete the empty methods and their declarations
| Column | Description |
|---|---|
| Unit | Source file name |
| Class | The form/frame class containing the method |
| Method | Name of the empty event handler |
| Line | Line number where the method is defined |
To properly remove an empty event handler:
- In the form designer, select the component
- In Object Inspector, find the event property
- Clear the event name (select and delete)
- Delete the method body from the source code
Or manually:
- Delete the method implementation from the implementation section
- Delete the method declaration from the class definition
- If the event is still linked in the DFM, clear it via Object Inspector
- Non-modal: The dialog stays open while you work in the IDE, allowing you to double-click items to navigate and fix issues without closing the detector
- Clears on close: Results are cleared when the dialog is closed; re-scan when you open it again
- Singleton: Clicking the menu item while the dialog is open brings it to front
- Enabled: Master switch for the feature
Some empty event handlers are intentional:
- Event handlers that deliberately do nothing to prevent default behavior
- Placeholder handlers for future implementation
- Handlers used with visual inheritance
Review each result before deleting.
Purpose: Visualizes unit dependencies within your project.
Default: OFF
Location: Options > DDevExtensions > Dependency Viewer
Access: Tools > DDevExtensions > Dependency Viewer...
- Tree view of all project units
- Shows interface and implementation uses clauses
- Uses / Used By view modes - toggle between forward and reverse dependencies
- Dependency depth indicator - shows each unit's position in the dependency chain
- Impact Analysis panel - shows direct/transitive dependents and risk level for selected unit
- Respect conditional compilation - optionally reads project defines and evaluates
{$IFDEF},{$IFNDEF},{$IF Defined(...)}blocks to exclude code that wouldn't compile for the current project - Circular reference detection with enhanced display
- Export circular references - save analysis to CSV or TXT for documentation and tracking
- Export Graph - export a Graphviz DOT dependency graph rendered as PNG (requires Graphviz)
- Layer violation detection - enforce architectural boundaries with configurable layer rules
- Double-click to open unit
- Enable in Options
- Open via Tools > DDevExtensions > Dependency Viewer...
- Click "Scan Project" to analyze dependencies
- Use the radio buttons to switch between "Uses" and "Used By" views
- Toggle "Show Depth" to display dependency depth numbers
| Mode | Description |
|---|---|
| Uses | Shows what units each unit depends on (forward dependencies) |
| Used By | Shows what units depend on each unit (reverse dependencies) |
The "Used By" view answers the question "What would break if I changed this unit?"
When "Show Depth" is enabled, each unit displays a depth number:
[0] SysUtils <- No project dependencies (only RTL/VCL)
[0] Classes <- No project dependencies
[1] MyUtils <- Depends only on depth-0 units
[2] DataModule <- Depends on MyUtils (depth 1)
[3] MainForm <- Depends on DataModule (depth 2)
Use cases for depth:
- Identify units that pull in long dependency chains
- Find natural architectural layers
- Spot potential circular dependency risks (units at same depth referencing each other)
Units involved in any circular reference are marked with (!):
(!) [1] ReportsFrm <- In at least one cycle
(!) [3] MainFrm <- In at least one cycle
[0] CompanyData <- Not in any cycle
[0] dmImages <- Not in any cycle
When expanded, child nodes show [interface] or [implementation]:
(!) [2] MyUnit
+-- [1] SysUtils [interface]
+-- [1] Classes [interface]
+-- [0] MyOtherUnit [implementation]
Circular references are displayed in the right panel with enhanced information:
UnitA -[I]-> UnitB -[impl]-> UnitC -[I]-> UnitA
| Marker | Meaning |
|---|---|
-[I]-> |
Dependency via interface uses clause |
-[impl]-> |
Dependency via implementation uses clause |
The "Circular References" label is color-coded by severity:
| Count | Color | Meaning |
|---|---|---|
| 0 | Green | No circular references - healthy codebase |
| 1-9 | Normal | Few cycles - manageable |
| 10-99 | Orange | Moderate cycles - attention needed |
| 100+ | Red | Many cycles - significant architectural issue |
This helps identify how to break the cycle - moving a reference from interface to implementation often resolves circular dependencies.
- Initial view: Units in any cycle show
(!)prefix automatically - Click a circular reference to mark those specific cycle members with
>>> <<<markers:>>> [1] ReportsFrm <<< - Double-click a circular reference to open the first unit in the IDE editor
- Click a different circular reference to update the markers
- Markers clear when you click elsewhere in the list
The tree panel automatically sizes to fit the longest unit name after scanning. The splitter remains draggable for manual adjustment.
Purpose: Answers the question "What breaks if I change this unit?"
When you click any unit in the tree, the Impact Analysis panel (top-right) shows how risky it would be to modify that unit.
What the fields mean:
| Field | What It Tells You |
|---|---|
| Unit | The unit you selected |
| Direct dependents | How many units have uses ThisUnit in their code |
| Total affected | All units that might need retesting if you change this unit |
| Risk | How careful you need to be (color-coded) |
Why is "Total affected" sometimes higher than "Direct dependents"?
This is the ripple effect. If you change UnitA:
- Units that directly use UnitA need retesting (direct dependents)
- But units that use those units might also be affected (transitive)
Example: You change dmData. Forms that use dmData might behave differently. And forms that use those forms might also be affected.
Risk Levels:
| Level | Color | Count | What To Do |
|---|---|---|---|
| Safe | Green | 0 | Change freely - nothing uses this unit |
| Low | Light Green | 1-3 | Low risk - minor testing needed |
| Medium | Orange | 4-10 | Be careful - test affected areas |
| High | Red | 11+ | High risk - thorough testing required |
Example:
Impact Analysis:
Unit: dmCurrent
Direct dependents: 37
Total affected: 38 units [Red] Risk: High
This tells you:
- 37 units have
uses dmCurrentin their code - 38 units total could be affected (37 direct + 1 that uses one of those 37)
- Risk is High - be very careful modifying this unit, test thoroughly
Compare to a safe unit:
Impact Analysis:
Unit: MyHelperUtils
Direct dependents: 0
Total affected: 0 units [Green] Risk: Safe
This unit can be changed freely - nothing depends on it.
When to use Impact Analysis:
- Before modifying a unit - Check the risk level first
- Before refactoring - Identify which units are safe to change
- Architecture review - Units with very high impact may be too central (tight coupling)
- Planning testing - Know which areas need testing after a change
Circular references can cause:
- Compilation order issues - the compiler may fail or produce unexpected results
- Initialization order problems - unit initialization sections may run in wrong order
- Tight coupling - makes code harder to maintain, test, and refactor
- Memory/resource issues - can prevent proper cleanup in finalization sections
-
Click on a circular reference to see which units are involved
-
Look at the arrows to understand the dependency direction:
UnitA -[impl]-> UnitB -[I]-> UnitAThis means: UnitA uses UnitB in implementation, UnitB uses UnitA in interface
-
Interface cycles are worse than implementation cycles:
-[I]->(interface) = tight coupling, harder to break-[impl]->(implementation) = looser coupling, often acceptable
1. Move uses to implementation section
// Before: UnitA interface uses UnitB
interface
uses UnitB; // Creates tight coupling
// After: Move to implementation if possible
implementation
uses UnitB; // Looser coupling, may break the cycle2. Extract shared types to a common unit
Before: UnitA <-> UnitB (both need TSharedType)
After: UnitA -> SharedTypes <- UnitB (no cycle)
3. Use interfaces instead of concrete classes
// Before: UnitA uses UnitB for TConcreteClass
// After: UnitA uses IMyInterface from a separate unit
// UnitB implements IMyInterface4. Pass dependencies as parameters
// Before: UnitB uses UnitA to access GlobalObject
// After: Pass the object as a parameter to UnitB's procedures5. Use events or callbacks
// Before: UnitB directly calls UnitA.DoSomething
// After: UnitB raises an event, UnitA subscribes to it- Start with interface cycles (
-[I]->) - these are the most problematic - Focus on units with many cycles - look for units appearing in multiple circular references
- Implementation-only cycles (
-[impl]->) are often acceptable and can be left alone - Short cycles (2 units) are usually easier to fix than long chains
- Enabled: Master switch for the feature
- Respect conditional compilation: When enabled, the scanner reads the project's conditional defines and evaluates
{$IFDEF},{$IFNDEF},{$IF},{$ELSE},{$ELSEIF}, and{$ENDIF}blocks. Uses clauses inside inactive conditional blocks are excluded from the dependency analysis.
When "Respect conditional compilation" is enabled (default), the Dependency Viewer:
-
Reads project defines from the
.dprojfile (e.g.,DEBUG,RELEASE,DBiAdmin) -
Evaluates conditional directives in the uses clauses:
{$IFDEF X}- includes if X is defined{$IFNDEF X}- includes if X is NOT defined{$IF Defined(X)}- includes if X is defined{$IF Defined(X) or Defined(Y)}- includes if X OR Y is defined{$IF Defined(X) and Defined(Y)}- includes if X AND Y are defined{$ELSE},{$ELSEIF},{$ENDIF}- handled correctly
-
Excludes inactive code from dependency analysis
Example:
uses
SysUtils,
{$IFDEF DBiAdmin}
AdminModule, // Only included when analyzing DBiAdmin project
{$ENDIF}
{$IFDEF DBiWorkflow}
WorkflowModule, // Only included when analyzing DBiWorkflow project
{$ENDIF}
CommonUnit;When analyzing the DBiAdmin project (which defines DBiAdmin), only SysUtils, AdminModule, and CommonUnit are included. WorkflowModule is excluded because DBiWorkflow is not defined.
Benefits:
- Eliminates false positive circular references caused by conditional code for other applications
- Shows accurate dependencies for the specific project being analyzed
- Useful for codebases with shared units used across multiple applications
When to Disable:
- If you want to see ALL possible dependencies regardless of current project defines
- If the project doesn't use conditional compilation in uses clauses
The Dependency Viewer can export circular reference analysis to external files for documentation, sharing with team members, or tracking progress over time.
How to Export:
- Click "Scan Project" to analyze dependencies
- Click the "Export..." button next to the circular references list
- Choose a location and filename
- Select the format (CSV or TXT)
CSV Format:
Cycle,Unit1,Arrow1,Unit2,Arrow2,Unit3,Arrow3,Unit4
1,UnitA,-[I]->,UnitB,-[impl]->,UnitA,,
2,FormA,-[I]->,FormB,-[I]->,FormC,-[impl]->,FormA- Each row represents one circular reference
- Units and arrows are in separate columns for easy filtering/sorting in Excel
-[I]->indicates interface dependency-[impl]->indicates implementation dependency
TXT Format:
Circular References Export
==========================
Generated: 2026-01-06 14:30:00
Project: MyProject.dproj
Total Circular References: 3
1. UnitA -[I]-> UnitB -[impl]-> UnitA
2. FormA -[I]-> FormB -[I]-> FormC -[impl]-> FormA
3. DataModule -[impl]-> ReportsFrm -[I]-> DataModule
- Human-readable format with header information
- Useful for documentation and code reviews
- Can be included in technical debt reports
Use Cases:
- Track circular reference count over time
- Share analysis with team members who don't have DDevExtensions
- Include in code review documentation
- Create baseline before refactoring efforts
The Dependency Viewer can export a visual dependency graph as a PNG image using Graphviz.
Prerequisite: Graphviz must be installed. Download it from: https://graphviz.org/download/
The "Export Graph..." button only appears when dot.exe is detected on the system (checked in common install locations and the system PATH).
How to Export:
- Click "Scan Project" to analyse dependencies
- Click the "Export Graph..." button (bottom of right panel)
- Choose a location and filename (saved as
.dotformat) - A PNG is automatically rendered and opened in your default image viewer
Graph Visual Conventions:
| Element | Meaning |
|---|---|
| Light green box | Project unit (source file found) |
| Light blue box | External/RTL unit (no source file) |
| Red-bordered box | Unit involved in a circular reference |
| Solid blue edge | Interface uses dependency |
| Dashed green edge | Implementation uses dependency |
A legend subgraph is included in the output explaining these conventions.
Example Output:
digraph Dependencies {
rankdir=LR;
MainForm [fillcolor=lightgreen];
DataModule [fillcolor=lightgreen];
SysUtils [fillcolor=lightblue];
MainForm -> DataModule [style=solid, color=blue];
DataModule -> SysUtils [style=dashed, color=forestgreen];
}
Use Cases:
- Get an at-a-glance architectural overview of your project
- Include dependency diagrams in project documentation
- Identify tightly coupled clusters of units visually
- Share architecture visualisations with team members
Version: 3.15.5 – 23 April 2026
Layer violation detection enforces architectural boundaries by checking that units only depend on units in allowed layers.
What is Layer Architecture?
Many projects organize code into logical layers:
┌─────────────────────────────────────┐
│ Presentation Layer │ Forms, Frames, UI components
├─────────────────────────────────────┤
│ Business Layer │ Business logic, rules, calculations
├─────────────────────────────────────┤
│ Data Layer │ Data modules, database access
├─────────────────────────────────────┤
│ Common/Shared │ Utilities, constants, types
└─────────────────────────────────────┘
Layer Rules:
- Presentation can use Business, Data, and Common
- Business can use Data and Common
- Data can use only Common
- Common has no project dependencies
What is a Layer Violation?
A layer violation occurs when a unit depends on a unit in a layer it shouldn't access:
❌ Data layer unit uses Presentation layer unit
dmData uses MainForm <- VIOLATION!
❌ Common layer unit uses Business layer unit
Utils uses Calculator <- VIOLATION!
Violation Detection Criteria:
A violation is reported when ALL of these conditions are met:
- Source unit matches a layer pattern - The unit containing the
usesclause is assigned to a layer - Target unit matches a different layer pattern - The unit being used is assigned to a different layer
- Dependency is not allowed - The checkbox in the dependency matrix (From row, To column) is unchecked
Units that don't match any layer pattern are ignored (no violations reported for them). Dependencies between units in the same layer are always allowed.
How to Configure Layers:
- Click "Layers..." button to open the Layer Configuration dialog
- Add layers with descriptive names (e.g., "Presentation", "Business", "Data", "Common")
- For each layer, define patterns to match unit names:
*Frm- matches any unit ending in "Frm"Frm*- matches any unit starting with "Frm"dm*- matches data modules*Utils- matches utility units- Wildcards:
*(any characters),?(single character)
- Use the dependency matrix to define allowed dependencies:
- Check the box where row layer can depend on column layer
- Unchecked = dependency not allowed (will report violations)
Example Configuration:
| Layer | Patterns |
|---|---|
| Presentation | *Frm, *Form, Frme* |
| Business | *Calc, *Manager, *Service |
| Data | dm*, *DataModule |
| Common | *Utils, *Consts, *Types |
Dependency Matrix:
| From \ To | Presentation | Business | Data | Common |
|---|---|---|---|---|
| Presentation | - | ✓ | ✓ | ✓ |
| Business | - | ✓ | ✓ | |
| Data | - | ✓ | ||
| Common | - |
How to Check for Violations:
- Configure layers (click "Layers...")
- Click "Scan Project" to analyze dependencies
- Click "Check Layers" to detect violations
- Violations appear in the Layer Violations list:
dmData -> MainFrm [interface] - Data cannot use Presentation Utils -> Calculator [implementation] - Common cannot use Business
Understanding Violation Display:
SourceUnit -> TargetUnit [clause] - FromLayer cannot use ToLayer
| Part | Meaning |
|---|---|
| SourceUnit | The unit with the forbidden dependency |
| TargetUnit | The unit being incorrectly used |
| [interface] | Dependency is in interface uses clause |
| [implementation] | Dependency is in implementation uses clause |
| FromLayer | The layer of the source unit |
| ToLayer | The layer of the target unit |
Real-World Example:
A data module dmCurrent was modified to include MainFrm in its implementation uses clause:
implementation
uses
CompanyData, UsersData, MainFrm, // <- violation added for testing
gllFunctions;After clicking "Check Layers", the detector reported:
Layer Violations: 1 found
dmCurrent (DataAccess) -> MainFrm (UI) [implementation]
What this tells us:
| Finding | Explanation |
|---|---|
dmCurrent |
The data module containing the problematic uses clause |
(DataAccess) |
Matched to DataAccess layer via the dm* pattern |
MainFrm |
The form unit being incorrectly referenced |
(UI) |
Matched to UI layer via the *Frm pattern |
[implementation] |
The dependency is in the implementation section (not interface) |
Why this is a violation: According to the default layer rules, DataAccess units can only depend on Core utilities. A data module should never reference a form directly - this creates tight coupling and makes the data module dependent on the UI layer, inverting the proper dependency direction.
How to fix: Remove MainFrm from dmCurrent's uses clause. If the data module needs to trigger UI actions, use events, callbacks, or an intermediary Business layer service instead.
Export Violations:
Click "Export..." next to the violations list to save violations to CSV or TXT format for documentation and tracking.
Default Configuration:
Click "Defaults" in the Layer Configuration dialog to load a standard 4-layer architecture:
- Presentation (forms, frames)
- Business (managers, services)
- Data (data modules)
- Common (utilities, constants)
Configuration Storage:
Layer configuration is saved to DDevExtensions_Layers.json in the application data directory and persists across IDE sessions.
Benefits:
- Enforce architectural boundaries automatically
- Catch violations early before they become embedded
- Document intended architecture through configuration
- Track architectural debt over time
- Guide new team members on proper dependencies
Purpose: Disables the IDE's package caching mechanism.
Default: OFF
Location: Options > DDevExtensions > Extended IDE Settings
Use Case: Resolves issues where packages aren't reloaded after changes.
Purpose: Prevents accidental triggering of the IDE's built-in source formatter (Ctrl+D by default).
Default: OFF
Location: Options > DDevExtensions > Extended IDE Settings
Note: Only affects the hotkey. You can still format via the menu.
Purpose: Prevents the debugger from breaking into child processes started by your application.
Default: OFF
Location: Options > DDevExtensions > Extended IDE Settings
Use Case: When debugging applications that launch other executables.
Purpose: Configures the behavior when double-clicking an editor tab.
Default: Zoom
Location: Options > DDevExtensions > Extended IDE Settings
| Setting | Behavior |
|---|---|
| None | No action |
| Zoom | Maximize editor, hide other panels |
| Super Zoom | Maximum editor space, minimal UI |
Usage: Double-click any editor tab to toggle zoom. Double-click again to restore.
Purpose: Provides additional keyboard shortcuts and improved cursor navigation for more efficient code editing.
Default: ON
Location: Options > DDevExtensions > Key Bindings
| Shortcut | Action | Option |
|---|---|---|
| Tab | Indent selected lines | Tab Indent |
| Shift+Tab | Unindent selected lines | Tab Indent |
| Home | Toggle between line start and first non-whitespace | Extended Home |
| Ctrl+Left/Right | Improved word navigation | Extended Ctrl+Left/Right |
| Ctrl+Alt+Shift+Up | Move line/block up | Move Line/Block |
| Ctrl+Alt+Shift+Down | Move line/block down | Move Line/Block |
| Ctrl+Alt+PgUp | Find declaration at cursor | Find Declaration on Caret |
| Ctrl+Shift+Up | Jump to interface section | Section Toggle |
| Ctrl+Shift+Down | Jump to implementation section | Section Toggle |
- Active: Master switch for all key bindings
- Tab Indent: Enable Tab/Shift+Tab to indent/unindent selected code
- Indent Single Line: Also indent when only one line is selected
- Extended Home: Toggle between BOL and first non-whitespace
- Switched Extended Home: Reverse the Extended Home behavior
- Extended Ctrl+Left/Right: Smarter word boundary detection
- Move Line/Block: Enable Ctrl+Alt+Shift+Up/Down to move code
- Find Declaration on Caret: Enable Ctrl+Alt+PgUp to find declaration
- Section Toggle: Enable Ctrl+Shift+Up/Down to jump between interface and implementation sections
-
Tab Indentation: Select multiple lines, then press Tab to indent or Shift+Tab to unindent. Works with block selections.
-
Extended Home: Press Home once to go to first non-whitespace character. Press again to go to column 1.
-
Move Line/Block: Place cursor on a line (or select multiple lines), then use Ctrl+Alt+Shift+Up/Down to move the entire block without cut/paste.
-
Section Toggle: Press Ctrl+Shift+Up to jump to the
interfacekeyword, or Ctrl+Shift+Down to jump to theimplementationkeyword. Works from anywhere in the unit.
On the 64-bit IDE, when a bound key falls through unhandled, DDevExtensions applies its built-in Home/Tab fallback directly instead of chaining to another plugin's binding for the same key. The legacy next-binding probe (GetNextBindingRec) crashes the 64-bit IDE — its TKeyBindingRec.Next field is 32-bit and cannot hold an x64 pointer — so it is used on the 32-bit IDE only (since 3.21.10).
Purpose: Monitors project directories in real-time for externally modified files and silently refreshes them in the IDE without requiring an Alt-Tab focus change.
Default: ON
Location: Options > DDevExtensions > External Mod Monitor
When a project is opened, the monitor begins watching its directory using the Windows ReadDirectoryChangesW API on a background thread. When a monitored file is modified externally (e.g., by an AI assistant, version control operation, or another editor), the change is detected and the file is silently refreshed in the IDE via IOTAModule.Refresh.
- Active: Enable or disable the monitor. Turn off if Embarcadero fixes the IDE's own detection.
- Debounce interval (ms): Time to wait after the last detected change before refreshing (default: 200ms). Prevents reload storms during batch operations like
git checkout. - Monitored extensions: Semicolon-separated list of file extensions to monitor (default:
.pas;.inc;.dfm;.dproj;.dpk). - Show notification on refresh (New in 3.13.4): When enabled, a Windows balloon notification appears listing the filenames that were refreshed. Shows up to 5 filenames with a summary for additional files. Enabled by default.
- Load grace period (ms) (New in 3.13.5): Time to suppress file change events after a project is opened (default: 3000ms). Prevents spurious notifications caused by the IDE normalising
.dprojfiles during project group loading. Minimum 500ms.
- Unsaved changes protected: Files with unsaved editor changes are never silently overwritten.
- Compile suppression: Monitoring is automatically suppressed during compilation to avoid false-positive reloads from intermediate build artifacts.
- Project load grace period: File change events arriving within the grace period after project load are silently discarded, preventing spurious notifications from IDE-initiated file writes.
- Reference counting: Multiple open projects sharing the same directory only create one watch.
- AI-assisted development: Claude Code or other AI tools edit source files while the IDE is focused — changes appear immediately.
- Git operations: After
git pull,git checkout, orgit stash pop, modified files refresh without Alt-Tab. - External editors: Editing a
.pasfile in Notepad, VS Code, or another tool while the IDE is open.
- Options checkbox (persisted in configuration XML)
- Environment variable:
DDevExtensions.DisabledFeatures=ExternalModMonitor
Purpose: Automatically removes unnecessary files after saving.
Default: ON
Location: Options > DDevExtensions > File Cleaner
.ddpfiles- Empty
Modelfolders - Empty
Historyfolders
Purpose: Replaces standard Find Unit and Use Unit dialogs with enhanced versions.
Default: ON
Access: File > Use Unit (Ctrl+Shift+A)
- Faster search
- Better filtering
- Preview of unit contents
- Fuzzy matching
Purpose: Terminates Document Explorer processes when closing the IDE.
Default: ON
Location: Options > DDevExtensions > Extended IDE Settings
Purpose: Alphabetises the member projects of the currently open project group so they list in name order in the Project Manager.
Default: ON
Location: Tools > Sort Projects in Group...
The IDE has no API for reordering a project group's members and only reads the order from the .groupproj when the group is opened. The command therefore:
- Saves the active project group.
- Writes a
.bakof the.groupproj, then rewrites it with the projects sorted — across all three places each project is listed (the<ItemGroup>, the per-project<Target>blocks, and theBuild/Clean/MakeCallTargetlists; the Project Manager order follows the<Target>/CallTargetsections, so sorting the<ItemGroup>alone has no effect). - Closes and reopens the group so the IDE reloads the new order, then reselects the previously active project.
A confirmation prompt warns that the group will be reloaded. The command no-ops (with a message) when the projects are already sorted or the group has fewer than two members. Disable via DDevExtensions.DisabledFeatures=ProjectGroupSorter.
Purpose: Manages and organizes Delphi IDE library paths with sorting, editing, and backup/restore capabilities.
Default: ON
Location: Tools > IDE Path Sorter... (below Build Tools...)
Dual-Panel Interface:
- Left Panel (Original): Shows current registry paths in read-only mode, with path count in label
- Right Panel (Working): Editable panel for arranging paths before applying, with path count in label
Path Type Support:
- Library Path
- Browsing Path
- Debug DCU Path
- HPP Output Directory
- Namespace Prefixes
- Package DCP Output
- Package DPL Output
- Translated Debug Library Path
- Translated Library Path
- Translated Resource Path
Platform Support:
- Win32, Win64, and any other platforms configured in the IDE
- Automatically detects available platforms from registry
Platform Category Filter (New in 3.13.4):
- Filter the Platform dropdown by category using checkboxes: All, Windows, Android, iOS, ARM, macOS, Linux
- All checkbox: when checked, all category checkboxes are checked and all platforms are shown
- Only categories with installed platforms appear as checkboxes
- Platforms containing "ARM" (WinArm64C, OSXARM64, iOSSimARM64) are grouped under ARM
- Windows appears first after All; remaining categories sorted alphabetically
- Checkbox selections persist across sessions via the registry
Editing Capabilities:
- Sort Alphabetically (A-Z button): One-click alphabetical sorting
- Move Up/Down/Top/Bottom: Reorder selected entry using buttons
- Drag and Drop: Reorder entries by dragging with mouse
- Delete Entry (right-click menu): Right-click any entry and select "Delete Entry" to remove it
- Copy Original (>> button): Reset working panel to original order
Duplicate Detection:
- Duplicate paths are highlighted in red bold text
- Click any entry in working panel to highlight all matching entries in original panel
- Helps identify and remove redundant paths
Path Count Display (New in 3.9.0):
- Both panel labels display the current path count (e.g., "Original Paths: 73", "Working Panel: 73")
- Counts update automatically when paths are loaded, copied, sorted, or deleted
- Makes it immediately visible if counts differ between panels
Deleted Entry Counter (New in 3.9.0):
- Tracks number of entries manually deleted via right-click menu
- Compares deleted count against actual difference (Original - Working)
- Normal display: "Deleted: 3" when counts match
- Warning display: "Deleted: 2 (Expected: 5) WARNING!" when counts don't match
- Mismatch indicates paths were lost unexpectedly (e.g., during sorting)
- Counter resets when paths are loaded or copied from original
Missing Path Detection (New in 3.9.0):
- Paths in the original panel that are missing from the working panel are highlighted with pink background and maroon bold text
- Warning message displayed in status bar when path counts differ between panels
- Right-click on original panel and select "Show Missing Paths..." to see a list of all missing paths
- Helps identify when paths are accidentally lost during sorting or editing
Resizable Panels (New in 3.9.0):
- Drag the splitter between the Original and Working panels to resize panel widths
- Minimum panel size prevents panels from becoming too small
Position/Size Persistence (New in 3.9.0):
- Form position, size, and panel width are saved when closing
- Settings restored when reopening the tool
- Form stays within screen bounds even if monitor configuration changes
Restart Reminder (New in 3.9.1):
- When closing the form after applying changes, a reminder dialog is shown
- Reminds user that Delphi must be restarted for the path changes to take effect
Backup System:
- Auto-backup: Automatically creates backup before applying changes (configurable checkbox)
- Manual backup: Create named backups at any time with "Create Backup" button
- Backup history: ListView showing all backups with timestamp, path type, platform, and description
- Restore: Select a backup and click "Restore Selected" to restore paths
- Delete backup: Remove old backups from history
Backup Storage:
%APPDATA%\DDevExtensions\LibraryPathBackups<DelphiVersion>.xml
- Open Tools > IDE Path Sorter...
- Select Path Type (e.g., Library Path) and Platform (e.g., Win32)
- Original paths appear in left panel, sorted copy in right panel
- Edit working panel:
- Use A-Z to sort alphabetically
- Use >> to copy original order
- Drag or use buttons to reorder
- Right-click and select "Delete Entry" to remove unwanted entries
- Review for duplicates (shown in red)
- Click Apply Working Panel to Registry to save changes
- Confirm the warning dialog
- Restart required: For path changes to take effect, you must close and reopen Delphi
- Path order matters: The first matching unit found wins during compilation
- RTL/VCL paths: Delphi's core paths should typically come first
- Vendor paths: Third-party component paths usually come after RTL/VCL
- Project paths: Custom project paths typically come last
- Always backup: Enable auto-backup to protect against mistakes
- Use this tool to clean up accumulated paths from uninstalled components
- Sort paths to make them easier to review and maintain
- Check for duplicates which can slow compilation and cause confusion
- Create a manual backup before major changes with a descriptive name
Purpose: Shortens the IDE's library path strings, rather than reordering them. Companion to the IDE Path Sorter.
Default: ON
Location: Tools > IDE Path Compactor... (below IDE Path Sorter...)
The tool reports two lengths before and after, because two separate limits exist and they are fixed by different means:
| Limit | Measured on | Macros help? |
|---|---|---|
| Stored length - the IDE's "path too long" warning | the raw registry string, macros unexpanded | Yes |
Expanded length - the dcc32/dcc64 command line |
the fully expanded string | No |
If you are hitting the compiler command-line limit, macro substitution will not help you - it shortens only the stored string. Both columns are still reported so that limit is visible rather than guessed.
Macro substitution. Finds directory prefixes repeated across entries and proposes $(NAME) variables for them. Saving is scored against the stored text each entry actually holds, so an entry already written as $(BDS)\source\rtl is left alone instead of being re-expressed - and made longer - under a new variable. Where a candidate prefix equals an existing macro's value, that macro is reused rather than a duplicate invented.
Cleanup. Reports, and optionally removes, three classes of entry.
All three removals are opt-in and unticked by default:
- Remove duplicate entries - compares expanded, normalised paths case-insensitively, within one path set only. Stronger than the Path Sorter's own check, which compares raw text and so cannot see
$(BDS)\sourceand the equivalent literal path as the same directory. - Remove entries whose directory is missing - platform-conditional folders are legitimately absent, and a folder on a disconnected share is not dead.
- Remove entries whose macro is undefined (dead) - the macro resolves nowhere at all.
Two safeguards apply to every removal:
- Re-verification immediately before writing. The analysis may be minutes old by the time you press Apply. A share can reconnect or an installer finish in between, so every entry marked for removal is re-tested - the file system re-probed, the macro re-resolved - and any that now passes is kept. You are told how many were rescued.
- An explicit list before deletion. Every entry to be removed is shown with its reason, and nothing is deleted until you confirm.
RAD Studio keeps two independent user-variable lists - Environment Variables for the 32-bit IDE and Environment Variables x64 for the 64-bit one - while the Library\<Platform> keys those macros resolve are shared between both IDEs.
A macro that this IDE cannot resolve but the other one can is therefore not dead: the entry still works in the other IDE. Such entries are reported as divergent and are never removed. The correct repair is to define the variable in both lists, which is what Apply does for every variable the Compactor creates.
Every affected value is backed up before anything is written, into the same history file the IDE Path Sorter uses - so one Restore list covers both tools. Nothing is written until Apply.
Apply is refused while the IDE's own Tools > Options dialog is open: that page holds its own in-memory copy of the library path and would write it back over your changes when committed.
An IDE restart is required after Apply. The running IDE captured its environment block at launch and will not see new variables until it restarts.
Purpose: Draws a faint horizontal line in the code editor above each top-level routine, so long units are easier to scan.
Default: ON
Location: Options > DDevExtensions > Method Separators
Credit: The idea comes from Tulip Method Separators by Samer Assil (MIT licence). This is an independent implementation built on DDevExtensions' own Pascal lexer; no Tulip source is included.
The line is drawn across the top edge of the first line of each block. When comments, compiler directives or attributes sit directly above a routine (no blank line between), the separator moves up above them, so a DocInsight block stays with the routine it documents. A comment trailing code on the line above is not counted.
Separated by default, in the implementation section only:
- Procedures, functions, constructors, destructors and operators, including
classmethods. Nested routines and anonymous methods are not separated. - Class, record, object and interface declarations that have a body. Forward declarations (
TFoo = class;), short declarations (EOops = class( Exception );) andclass oftypes are not. initialization,finalization, and a program's mainbeginblock.
Alternative headers for one routine under {$IFDEF} / {$ELSE} count as one routine. Only .pas, .dpr and .dpk files are marked; include files are not.
The editor buffer is read at most once per paint cycle and at most four times a second, and is rescanned only when its text has changed. Typing, pasting and undo all update the lines within about a third of a second.
- Active: Shows or hides the separators. Takes effect immediately; no IDE restart.
- Separate class, record and interface declarations: Default on.
- Separate initialization, finalization and main program block: Default on.
- Also separate declarations in the interface section: Default off. When on, types and standalone routine declarations in the interface section are separated; the methods declared inside a class are not.
- Line style: Dotted (default), dashed or solid.
- Custom colour: By default the colour is derived from the editor background: darker on a light theme, lighter on a dark one. A custom colour too close to the background to be seen is replaced by the automatic colour.
- Options checkbox (persisted in configuration XML)
- Environment variable:
DDevExtensions.DisabledFeatures=MethodSeparators
Delphi 11 or later: the code-editor paint API (ToolsAPI.Editor) first shipped in Delphi 11, so the Delphi 10.x projects cannot have it. Tested on Delphi 13 (D_D130); listed in D_D110 and D_D120 too, which cannot be built on the build machine and are untested. Drawn through the IDE's code-editor paint notifier (INTACodeEditorEvents), in the 32-bit and 64-bit IDE alike.
Purpose: Clears the compiler's internal unit cache before each build.
Default: OFF
Location: Options > DDevExtensions > Compile Progress
- Standard: Releases basic cache
- High: More aggressive cache release
Use Case: Helps resolve rare caching issues where the compiler uses stale unit information.
Purpose: Prevents ExplicitLeft, ExplicitTop, ExplicitWidth, and ExplicitHeight properties from being saved to DFM files.
Default: OFF
Location: Options > DDevExtensions > Form Designer
- Reduces DFM file size
- Eliminates meaningless changes in version control
- Cleaner form files
These properties are used by the IDE for anchor calculations. Only enable if you understand the implications.
Purpose: Prevents the PixelsPerInch property from being saved to DFM files.
Default: OFF
Location: Options > DDevExtensions > Form Designer
- Eliminates DPI-related phantom changes in version control
- Developers with different monitor DPI settings won't cause unwanted DFM modifications
- Forms opened on 4K monitors won't conflict with forms saved on standard monitors
When enabled, the PixelsPerInch property is read from existing DFM files (for backward compatibility) but never written when saving. The IDE recalculates scaling at runtime based on the current display settings.
Note — only observable at non-96 DPI. Delphi only writes
PixelsPerInchto a DFM when the design-time DPI differs from 96 (the standard 100% scaling baseline). The RTL streams it conditionally:PixelsPerInch <> USER_DEFAULT_SCREEN_DPI(96) — seeSystem.Classes.TDataModule.DefineProperties(and the equivalent inTCustomForm). Consequently, when you design at 100% scaling (96 DPI) the IDE never emitsPixelsPerInchin the first place, so this option has no visible effect there — there is nothing to strip. The feature only does observable work (and only matters for version control) when forms/data modules are designed on a scaled / high-DPI display (e.g. 125% → 120 DPI, 150% → 144 DPI). This is also why a fresh form or data module saved at 96 DPI shows noPixelsPerInchline regardless of this setting.
This feature works in all supported Delphi versions:
| Delphi Version | Behavior |
|---|---|
| 11.0+ (Alexandria) | Full support - reads, applies, and strips PixelsPerInch using native RTL methods |
| 10.2-10.4 (Tokyo, Rio, Sydney) | Compatibility mode - reads and discards PixelsPerInch from DFM files, never writes it |
Cross-version team benefit: If team members use different Delphi versions, enabling this feature ensures:
- Delphi 11+ users can open DFMs without PixelsPerInch causing issues
- Delphi 10.2-10.4 users can open DFMs that were created in Delphi 11+ (which contain PixelsPerInch) without "Unknown property" errors
- All saved DFMs will have PixelsPerInch stripped, maintaining consistency
- Teams with developers using different DPI/monitor configurations
- Projects experiencing frequent PixelsPerInch merge conflicts
- When using High DPI scaling features (Delphi 10.3+)
- Teams with mixed Delphi versions (10.2-10.4 and 11+)
Purpose: Prevents the TextHeight property from being saved to DFM files.
Default: OFF
Location: Options > DDevExtensions > Form Designer
- Eliminates font-rendering related phantom changes
- Different machines with different font configurations won't cause unwanted DFM modifications
- Cleaner version control diffs
When enabled, the TextHeight property is read from existing DFM files but never written when saving. TextHeight is purely informational and is recalculated at runtime based on the form's font.
- Teams experiencing frequent TextHeight changes in version control
- When DFM files show modifications after simply opening a form
- Projects where TextHeight changes cause merge conflicts
Purpose: Enhances the "Open File At Cursor" feature with better file resolution and namespace support.
Default: OFF
Location: Options > DDevExtensions > Extended IDE Settings
- Searches project paths, library paths, and browsing paths
- Supports XE2+ unit namespaces (e.g.,
System.SysUtils) - Searches across all projects in the project group
- Falls back to standard file dialog if not found
Purpose: Shows forms and data modules from all loaded packages in inheritance dialogs.
Default: OFF
Location: Options > DDevExtensions > Extended IDE Settings
Purpose: Highlights and expands the project containing the currently active file in Project Manager.
Default: ON
Location: Options > DDevExtensions > Extended IDE Settings
Purpose: Adds a search/filter box to the Structure View panel for quickly finding methods, properties, and other elements.
Default: OFF (no hotkey assigned)
Location: Options > DDevExtensions > Extended IDE Settings
- Assign a hotkey in Options (e.g., Ctrl+Shift+S)
- With Structure View visible, press the hotkey
- Type to filter the structure tree
- Press Enter to navigate to the selected item
Purpose: When compiling a file that belongs to a different project, prompts to switch to that project.
Default: ON
Location: Options > DDevExtensions > Compile Progress
Purpose: Automatically sets the bottom margin of TLabel components to zero when created in the form designer.
Default: ON
Location: Options > DDevExtensions > Form Designer
Why: Prevents unexpected spacing issues in form layouts when using alignment features.
Purpose: Scans your project for TODO, FIXME, and other comment markers.
Default: ON
Location: Options > DDevExtensions > TODO Aggregator
Access: Tools > DDevExtensions > TODO/FIXME Aggregator...
| Pattern | Typical Use |
|---|---|
| TODO | Task to be completed |
| FIXME | Known bug to fix |
| HACK | Temporary workaround |
| BUG | Documented bug |
| NOTE | Important information |
| XXX | Attention needed |
Add priority in parentheses:
// TODO(high): Implement error handling
// FIXME(low): Cosmetic issue
// HACK(medium): Temporary workaround- Filter by category (TODO, FIXME, etc.)
- Filter by priority (High, Medium, Low)
- Sortable columns
- Double-click to navigate to source
- Export to CSV
- Enabled: Master switch
- Patterns: Comma-separated list of patterns to detect
Default patterns: TODO,FIXME,HACK,BUG,NOTE,XXX
| Column | Description |
|---|---|
| Unit | Source file name |
| Category | TODO, FIXME, HACK, etc. |
| Priority | High, Medium, Low, or (none) |
| Line | Line number in source |
| Text | The comment text |
Purpose: Finds code that can never execute because it follows a statement that always exits the current scope.
Default: ON
Location: Options > DDevExtensions > Unreachable Code Detector
Access: Tools > DDevExtensions > Unreachable Code Detector...
- Scans all Pascal units in the active project
- Displays the project name being analyzed
- Smart detection that ignores conditional terminators
- Filter results by reason type
- Double-click to navigate to source
- Export to CSV
Code that appears after these unconditional control flow statements:
| Statement | Description |
|---|---|
| Exit | Returns from the current procedure/function |
| Raise | Throws an exception |
| Break | Exits the current loop |
| Continue | Skips to next loop iteration |
| Halt | Terminates the program |
| Abort | Raises a silent exception |
procedure Example1;
begin
Exit;
ShowMessage('Never shown'); // Unreachable - after Exit
end;
procedure Example2;
begin
raise Exception.Create('Error');
CleanupResources; // Unreachable - after Raise
end;
procedure Example3;
var
I: Integer;
begin
for I := 1 to 10 do
begin
if I = 5 then
Break;
DoSomething; // Unreachable - after Break (missing begin/end)
end;
end;The detector is smart enough to recognize conditional terminators and will NOT flag code after them as unreachable:
// These are CONDITIONAL - the code after IS reachable:
if X > 10 then Exit; // Exit only if X > 10
ShowMessage('Still runs'); // This is reachable - NOT flagged
if Error then raise Exception.Create('Oops');
DoCleanup; // This is reachable - NOT flagged
for I := 1 to 10 do
begin
if Found then Break; // Break only if Found
ProcessItem(I); // This is reachable - NOT flagged
end;The detector only flags code after unconditional terminators:
// These are UNCONDITIONAL - the code after is NOT reachable:
Exit;
ShowMessage('Never runs'); // FLAGGED - truly unreachable
raise Exception.Create('Error');
DoCleanup; // FLAGGED - truly unreachable| Column | Description |
|---|---|
| Unit | Source file name |
| Line | Line number of the unreachable code |
| Reason | Why the code is unreachable (After Exit, After Raise, etc.) |
| Code | The unreachable statement |
- Enabled: Master switch
- Check After Exit: Detect code after Exit statements
- Check After Raise: Detect code after Raise statements
- Check After Break: Detect code after Break statements
- Check After Continue: Detect code after Continue statements
- Check After Halt: Detect code after Halt calls
- Check After Abort: Detect code after Abort calls
- Forgotten code - Old code left behind after refactoring
- Missing begin/end - Incorrect block structure makes code appear to follow a control statement
- Debug code - Temporary Exit/Break added during debugging and not removed
- Copy/paste errors - Code duplicated incorrectly
The detector includes several smart features to minimize false positives:
- Conditional terminators: Code after
if X then Exit;is correctly recognized as reachable - Case statement branches: Terminators inside case branches (e.g.,
0: Exit;) don't flag subsequent case labels as unreachable - Variables named after keywords: Assignments like
Continue := False;are recognized as variable assignments, not control flow - Parameter declarations:
var Continue: Booleanin procedure parameters is recognized as a parameter name - String literals: Keywords inside strings (including Delphi 12+ triple-quoted multi-line strings) are properly skipped
- Comments: Keywords inside comments are ignored
- Interface section: Only the implementation section is scanned (no executable code in interface)
- Conditional compilation: Reads project defines and correctly evaluates
{$IFDEF},{$IFNDEF}, and simple{$IF Defined(X)}blocks
The detector reads the project's conditional defines and intelligently handles conditional compilation:
| Directive | Behavior |
|---|---|
{$IFDEF X} |
If X is defined in project, scan the block; otherwise skip to {$ELSE} or {$ENDIF} |
{$IFNDEF X} |
If X is NOT defined, scan the block; otherwise skip |
{$IF Defined(X)} |
Evaluates simple conditions; scans if true, skips if false |
{$IF NOT Defined(X)} |
Evaluates negated conditions correctly |
{$ELSE} |
Switches between scanning and skipping appropriately |
{$ENDIF} |
Ends the conditional block |
Example: If your project defines DBiUsers, then code inside {$IFDEF DBiUsers} will be scanned, while code inside {$IFNDEF DBiUsers} will be skipped.
The detector has the following limitations:
| Limitation | Description |
|---|---|
Complex {$IF} expressions |
Expressions with and/or operators (e.g., {$IF Defined(A) or Defined(B)}) cannot be fully evaluated; the block is skipped to avoid false positives. |
| Assembly blocks | Inline assembly with JMP or other branch instructions is not analyzed. |
| Complex conditionals | Only simple if X then Terminator; patterns are detected. Multi-statement conditionals may not be recognized. |
If you encounter false positives:
- Complex
{$IF}expressions: Code in blocks with complex boolean expressions is skipped rather than potentially flagging incorrectly - Intentional unreachable code: Sometimes unreachable code is left for defensive programming or future use
- Complex control flow: Nested conditions or unusual patterns may confuse the detector
These are typically few in number and can be noted and ignored.
This is a static analysis tool and is not perfect. It may not catch all cases of unreachable code, and in rare situations may report false positives. Always manually verify results before making code changes. Use it as a guide to identify potential issues, not as an authoritative source.
Purpose: Finds units in uses clauses that aren't actually referenced in code.
Default: OFF
Location: Options > DDevExtensions > Unused Unit Detector
Access: Tools > DDevExtensions > Unused Unit Detector...
- Scans interface and implementation uses clauses
- Shows file, unit name, section, and line number
- Double-click to navigate to the uses clause
- Right-click to add to ignore list
- Export to CSV
- Enabled: Master switch
- Ignore List: Units to skip (one per line)
Common units to ignore:
System
SysUtils
Classes
Windows
| Column | Description |
|---|---|
| Source File | The file containing the unused unit |
| Unused Unit | Name of the potentially unused unit |
| Section | Interface or Implementation |
| Line | Line number of the uses clause |
Some units may appear unused but are required:
- Component registration units
- Units that register classes for streaming
- Units included for side effects (initialization sections)
Use "Add to Ignore List" for these cases.
Purpose: Automatically analyzes which symbols from each unit are used in the interface vs implementation sections, then recommends moving units to their optimal uses clause location.
Default: ON
Location: Options > DDevExtensions > Uses Clause Manager
Access: Tools > DDevExtensions > Uses Clause Manager...
Proper uses clause organization provides several benefits:
- Faster compilation - Units in the implementation section don't need to be compiled when only the interface changes
- Better encapsulation - Reduces coupling by only exposing necessary dependencies in the interface
- Cleaner architecture - Makes unit dependencies clearer and easier to understand
- Smaller interface sections - Interface section stays focused on the public API
The Uses Clause Manager works in three steps:
Step 1: Build Exports Database
- Scans all units in the project's search paths
- Parses each unit's interface section to extract exported identifiers
- Builds a database mapping identifiers to their source units
- This only needs to be done once (or when search paths change)
Step 2: Analyze Current Unit
- Parses the current unit with the Delphi lexer
- Tracks all identifiers used in the interface section
- Tracks all identifiers used in the implementation section
- Matches identifiers to their source units using the exports database
Step 3: Generate Recommendations
- For each unit in the uses clauses:
- If ANY identifier from that unit appears in the interface section → recommend interface
- If ALL identifiers appear only in implementation → recommend implementation
- Shows clear reasoning for each recommendation
- Open a Pascal unit in the editor
- Open Uses Clause Manager via Tools menu
- Click "Build Database" (first time only)
- Shows progress as it scans units
- Shows summary when complete (e.g., "Database built: 450 units scanned")
- Database is cached for subsequent analyses
- Click "Analyze Unit"
- Analyzes the current file
- Populates the list with recommendations
- Review recommendations
- Select a unit to see which identifiers are used where
- Units marked for change have different current/recommended sections
- Apply changes using one of two methods:
- "Apply Changes" button - applies all recommendations at once
- Right-click → "Move to Recommended Section" - moves only selected unit(s)
- Both methods support undo (Ctrl+Z)
| Column | Description |
|---|---|
| Unit | Name of the used unit |
| Current | Where the unit is currently (interface/implementation) |
| Recommended | Where the unit should be |
| Reason | Why the recommendation was made |
| Reason | Current | Recommended | Action |
|---|---|---|---|
| OK - used in both sections | interface | interface | No action needed. Unit is correctly placed. |
| OK - used in interface section | interface | interface | No action needed. Unit is correctly placed. |
| OK - only used in implementation | implementation | implementation | No action needed. Unit is correctly placed. |
| Only used in implementation section | interface | implementation | Move to implementation. Right-click → Move to Recommended Section. |
| Identifiers used in interface section | implementation | interface | Move to interface. This unit provides types/constants used in class declarations or function signatures. |
| No direct usage detected - review manually | interface | implementation | Review carefully. The unit may be unused, or it may be used implicitly (e.g., component registration, initialization side effects). Check before removing. |
Color Guide:
- Rows where Current = Recommended need no action
- Rows where Current ≠ Recommended should be reviewed and potentially moved
When you select a unit in the list, the details panel shows:
Unit: SysUtils
Identifiers used in INTERFACE section:
TStringList, TNotifyEvent
Identifiers used in IMPLEMENTATION section:
Format, IntToStr, FileExists
Recommendation: Keep in interface (types used in interface)
This helps you understand why each recommendation is made.
The tool uses the following logic:
| Scenario | Recommendation |
|---|---|
| Any identifier in interface | Keep in interface uses |
| All identifiers in implementation only | Move to implementation uses |
| No identifiers found | Move to implementation (may be unused) |
When the same identifier is exported by multiple units (e.g., Format from both SysUtils and a custom unit), the tool uses priority:
- RTL/VCL units take priority - System, SysUtils, Classes, etc.
- Closer units second - Units in the same directory
- Others last - Third-party and distant units
This matches how Delphi resolves identifiers at compile time.
These units are automatically prioritized for ambiguous identifiers:
System, SysUtils, Classes, Types, TypInfo,
Windows, Messages,
Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Graphics,
Vcl.Dialogs, Vcl.Menus, Vcl.ComCtrls, Vcl.Grids,
Data.DB,
Generics.Collections, Generics.Defaults
- Export: Save results to CSV for documentation or further analysis
- Copy to Clipboard: Copy selected items in tab-separated format
- Enabled: Master switch for the feature
| Limitation | Description |
|---|---|
| Ambiguous identifiers | Same identifier exported by multiple units may not resolve correctly without type information |
| Implicit uses | System unit is implicitly used and not analyzed |
| Generic types | TList<T> and similar generic instantiations require full type analysis |
| First scan is slow | Building the exports database requires parsing many files (subsequent analyses are fast) |
| Cross-project | Only analyzes the current project's search path |
- Build database after opening project - Ensures all search paths are included
- Rebuild database when search paths change - Click Build Database again
- Review before applying - Check that recommendations make sense for your code
- Use undo if needed - Ctrl+Z reverts the changes immediately
- Re-analyze after applying - Verify the new state
Scenario 1: Form with data module
Before:
interface uses DataModule; // Only used in implementation
After:
implementation uses DataModule; // Moved - cleaner interface
Scenario 2: Type used in interface
Before:
implementation uses MyTypes; // TMyRecord used in interface
After:
interface uses MyTypes; // Moved - required for compilation
Scenario 3: Mixed usage
Unit SysUtils:
- TStringList used in interface (type declaration)
- Format used in implementation (code)
Recommendation: Keep in interface (interface usage takes priority)
Purpose: Finds the groups of units in the active project that depend on each other, ranks them by how dangerous they are, and says what it would take to break each link - down to the identifiers that hold it in place.
Default: ON
Location: Options > DDevExtensions > Decirculariser
Access: Tools > DDevExtensions > Decirculariser...
- Groups, not cycles. A group of mutually dependent units contains a huge number of distinct cycles, so listing them is useless. The group is the honest answer to "these units are stuck together".
- FRAGILE groups are listed first. A cycle through
implementationsections is legal Delphi and costs nothing. The danger is a group held together by exactly ONE implementation-section use while the other direction is already an interface use: the day someone needs that type in the interface section, the build stops withE2004 Circular unit reference, with no warning beforehand. A two-unit FRAGILE pair therefore outranks a large benign tangle.
- Top list: one row per group - its risk (
FRAGILE,benign, orUNCOMPILABLEwhen only part of a project was read), the number of units, the interface and implementation edges inside it, and its members. - Bottom list: the implementation-section edges of the selected group, cheapest to break first, each with its verdict:
| Verdict | Meaning | Fix |
|---|---|---|
| dead uses entry | Nothing of the target unit is referenced | auto, or by hand (start-up code) - see below |
| cannot tell | Only identifiers that several units declare are referenced (inherited form members, most often) | Remove the entry and let the compiler decide |
| extractable | A few identifiers cross the edge, named in the details | Move them to a shared leaf unit |
| collaboration | Many identifiers cross it | A design decision: an interface, an event or a registry |
- Details: the full advice for the selected edge and the identifiers that cross it.
- Double-click an edge (or right-click > Open File) to go to its uses entry. Copy report puts a plain-text report on the clipboard.
Only rows marked auto can be removed: a dead uses entry to a unit with no start-up code. A unit with an initialization or finalization section, or a class constructor or destructor, is by hand (start-up code) instead: the entry may be what links that code in or what decides the order it runs in, and removing it still compiles, so nothing would tell you.
Select one or more auto rows and click Remove dead entries.... Before changing anything the project is analysed again from the current editor buffers, and only entries that are still dead are removed. Each removal deletes just that entry (its comma, any in '...' part, and its line when the line is left empty), and the only entry of a clause takes the whole uses clause with it. An entry is refused, and reported, when the clause holds a compiler directive or a comment lies inside the text to be deleted.
The changed units are left modified in the editor and not saved: compile to confirm, then save, or undo with Ctrl+Z.
- Enabled: Master switch
- Extractable threshold: at or below this many crossing identifiers an edge is
extractablerather thancollaboration(default 3)
- Only the project's own
.pasunits are analysed; the RTL, the VCL and units outside the project are not things you can restructure here. - An old-style unit body (
begin ... end.in place of aninitializationsection) is not recognised as start-up code.
- Ensure DDevExtensions is properly installed
- Check if the feature is enabled in Options
- Restart the IDE after changing settings
Features accessed via the Tools > DDevExtensions submenu must be enabled in Options first:
- Build Statistics
- Code Style Checker
- Dead Code Detector
- DFM/PAS Consistency Checker
- Dependency Viewer
- Empty Event Handler Detector
- TODO/FIXME Aggregator
- Unreachable Code Detector
- Unused Unit Detector
- Uses Clause Manager
- Check that the feature is enabled
- Verify the hotkey isn't conflicting with another binding
- Some hotkeys only work in specific contexts (editor, form designer)
- Ensure a project is open and active
- Click the "Scan" or "Analyze" button
- Check filter settings aren't hiding results
This indicates the source file wasn't found. Causes:
- External/RTL units without source
- Incorrect library paths
- Source files moved or deleted
If the IDE becomes slow:
- Disable features you don't use
- For large projects, disable auto-show for Build Statistics
- Use filters in analysis tools to limit scope
DDevExtensions - Enhancing RAD Studio since Delphi 2007
Original author: Andreas Hausladen Website: https://www.idefixpack.de/ddev
