{"id":13803550,"url":"https://github.com/dlang-community/D-Scanner","last_synced_at":"2025-05-13T16:32:00.849Z","repository":{"id":3072977,"uuid":"4096145","full_name":"dlang-community/D-Scanner","owner":"dlang-community","description":"Swiss-army knife for D source code","archived":false,"fork":false,"pushed_at":"2025-03-23T12:56:16.000Z","size":4037,"stargazers_count":244,"open_issues_count":124,"forks_count":80,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-05-12T00:47:21.973Z","etag":null,"topics":["ctags","dlang","lint","linter","static-analysis","syntax-checker"],"latest_commit_sha":null,"homepage":null,"language":"D","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dlang-community.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE_1_0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-04-21T13:04:35.000Z","updated_at":"2025-04-24T08:44:51.000Z","dependencies_parsed_at":"2023-09-24T16:29:04.855Z","dependency_job_id":"c4c0cfce-88f8-45fa-89f8-665beb6e43b3","html_url":"https://github.com/dlang-community/D-Scanner","commit_stats":{"total_commits":1145,"total_committers":68,"mean_commits":16.83823529411765,"dds":"0.37292576419213974","last_synced_commit":"9b171c46d2cfc65800c5b0ec0c7d8db074a3d961"},"previous_names":[],"tags_count":67,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlang-community%2FD-Scanner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlang-community%2FD-Scanner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlang-community%2FD-Scanner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlang-community%2FD-Scanner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dlang-community","download_url":"https://codeload.github.com/dlang-community/D-Scanner/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253981850,"owners_count":21994347,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ctags","dlang","lint","linter","static-analysis","syntax-checker"],"created_at":"2024-08-04T01:00:35.278Z","updated_at":"2025-05-13T16:31:58.519Z","avatar_url":"https://github.com/dlang-community.png","language":"D","readme":"# D-Scanner\n\n[![CI status](https://github.com/dlang-community/D-Scanner/actions/workflows/default.yml/badge.svg)](https://github.com/dlang-community/D-Scanner/actions?query=workflow%3A%22run+tests%22)\n[![Latest version](https://img.shields.io/dub/v/dscanner.svg)](http://code.dlang.org/packages/dscanner)\n[![License](https://img.shields.io/dub/l/dscanner.svg)](http://code.dlang.org/packages/dscanner)\n\nD-Scanner is a tool for analyzing D source code\n\n### Building and installing\nFirst make sure that you have all the source code. Run ```git submodule update --init --recursive```\nafter cloning the project.\n\nTo build D-Scanner, run ```make``` (or the build.bat file on Windows).\nThe build time can be rather long with the -inline flag on front-end versions\nolder than 2.066, so you may wish to remove it from the build script. The\nmakefile has \"ldc\" and \"gdc\" targets if you'd prefer to compile with one of these\ncompilers instead of DMD. To install, simply place the generated binary (in the\n\"bin\" folder) somewhere on your $PATH.\n\n### Testing\nTesting does not work with DUB.\nUnder linux or OSX run the tests with `make test`.\nUnder Windows run the tests with `build.bat test`.\n\n### Installing with DUB\n\n```sh\n\u003e dub fetch dscanner \u0026\u0026 dub run dscanner\n```\n\n## Installing with Docker\n\nWith Docker no installation is required:\n\n```sh\ndocker run --rm -v $(pwd):/src dlangcommunity/dscanner\n```\n\n# Usage\nThe following examples assume that we are analyzing a simple file called helloworld.d\n\n```d\nimport std.stdio;\nvoid main(string[] args)\n{\n\twriteln(\"Hello World\");\n}\n```\n\n## Linting\n\nUse\n\n```sh\ndscanner lint source/\n```\n\nto view a human readable list of issues.\n\nDiagnostic types can be enabled/disabled using a configuration file, check out\nthe `--config` argument / `dscanner.ini` file for more info. Tip: some IDEs that\nintegrate D-Scanner may have helpers to configure the diagnostics or help\ngenerate the dscanner.ini file.\n\u003c!--\nIDE list for overview:\ncode-d has an \"insert default dscanner.ini content\" command + proprietary\n\tdisabling per-line (we really need to bring that into standard D-Scanner)\n--\u003e\n\n## Auto-Fixing issues\n\nUse\n\n```sh\ndscanner fix source/\n```\n\nto interactively fix all fixable issues within the source directory. Call with\n`--applySingle` to automatically apply fixes that don't have multiple automatic\nsolutions.\n\n## Tooling integration\n\nMany D editors already ship with D-Scanner.\n\nFor a CLI / tool parsable output use either\n\n```sh\ndscanner -S source/\n# or\ndscanner --report source/\n```\n\nThe `--report` switch includes all information, plus cheap to compute autofixes\nthat are already resolved ahead of time, as well as the names for the autofixes\nthat need to be resolved using the `--resolveMessage` switch like described\nbelow.\n\nYou can also specify custom formats using `-f` / `--errorFormat`, where there\nare also built-in formats for GitHub Actions:\n\n```sh\n# for GitHub actions: (automatically adds annotations to files in PRs)\ndscanner -S -f github source/\n# custom format:\ndscanner -S -f '{filepath}({line}:{column})[{type}]: {message}' source/\n```\n\nTo resolve automatic issue fixes for a given location use\n\n```sh\n# collecting automatic issue fixes\n# --resolveMessage \u003cline\u003e:\u003ccolumn\u003e \u003cfilename\u003e\ndscanner --resolveMessage 11:3 file.d\n# --resolveMessage b\u003cbyteIndex\u003e \u003cfilename\u003e\ndscanner --resolveMessage b512 file.d\n# \u003cfilename\u003e may be omitted to read from stdin\n```\n\noutputs JSON:\n\n```json\n// list of available auto-fixes at the given location\n[\n\t{\n\t\t\"name\": \"Make function const\",\n\t\t// byte range `[start, end)` what code to replace\n\t\t// this is sorted by range[0]\n\t\t\"replacements\": [\n\t\t\t// replace: range[0] \u003c range[1], newText != \"\"\n\t\t\t{\"range\": [10, 14], \"newText\": \"const \"},\n\t\t\t// insert: range[0] == range[1], newText != \"\"\n\t\t\t{\"range\": [20, 20], \"newText\": \"auto\"},\n\t\t\t// remove: range[0] \u003c range[1], newText == \"\"\n\t\t\t{\"range\": [30, 40], \"newText\": \"\"},\n\t\t]\n\t}\n]\n```\n\nAlgorithm to apply replacements:\n```d\nforeach_reverse (r; replacements)\n\tcodeBytes = codeBytes[0 .. r.range[0]] ~ r.newText ~ codeBytes[r.range[1] .. $];\n```\n\nReplacements are non-overlapping, sorted by `range[0]` in ascending order. When\ncombining multiple different replacements, you first need to sort them by\n`range[0]` to apply using the algorithm above.\n\n## Other features\n\n### Token Count\nThe \"--tokenCount\" or \"-t\" option prints the number of tokens in the given file\n\n\t$ dscanner --tokenCount helloworld.d\n\t20\n\n### Import Listing\nThe \"--imports\" or \"-i\" option prints a listing of modules imported by the given\nsource file.\n\n\t$ dscanner --imports helloworld.d\n\tstd.stdio\n\nPassing \"-I\" arguments (import locations) will cause D-Scanner to also attempt\nto resolve the locations of the imported modules.\n\n    $ dscanner --imports helloworld.d -I ~/.dvm/compilers/dmd-2.071.1-b2/src/phobos/ -I ~/.dvm/compilers/dmd-2.071.1-b2/src/druntime/src/\n\t/home/brian/.dvm/compilers/dmd-2.071.1-b2/src/phobos/std/stdio.d\n\nRemember to pass map the import locations when you use Docker:\n\n\tdocker run --rm -v $(pwd):/src -v /usr/include/dlang/dmd:/d dlangcommunity/dscanner --imports helloworld.d -I/d\n\t/d/std/stdio.d\n\nThe \"--recursiveImports\" option is similar to \"--imports\", except that it lists\nimports of imports (and so on) recursively. The recursive import option requires\nimport paths to be specified in order to work correctly.\n\nLimitations:\n* The import listing feature DOES NOT IGNORE imports that may be unused to to `version` or `static if`.\n* The import listing DOES NOT INCLUDE imports introduced by mixins.\n\n### Syntax Check\nThe \"--syntaxCheck\" or \"-s\" option prints a listing of any errors or warnings found\nwhile lexing or parsing the given source file. It does not do any semantic\nanalysis and it does not compile the code. The format of the errors or\nwarnings can be configured with the \"--errorFormat\" or \"-f\" option.\n\n### Style Check\nThe \"--styleCheck\" or \"-S\" option runs some basic static analysis checks against\nthe given source files, the sources contained in the given folders, or the sources contained in the current working directory (when nothing is supplied).\nThe format of the errors or warnings can be configured with the \"--errorFormat\" or \"-f\" option.\n\n#### Skip style checks in the tests\nStatic checks in the unit tests can produce irrelevant warnings. For example,\nit's legit to declare a variable that's not used if the goal is to verify that\na templatized function can be instantiated by inference of the type of this variable.\nTo avoid these cases, it's possible to pass the \"--skipTests\" option.\n\n#### Configuration\nBy default all checks are enabled. Individual checks can be enabled or disabled\nby using a configuration file. Such a file can be placed, for example, is the root directory of your project.\nRunning `dscanner --defaultConfig` will generate a default configuration file and print the file's location.\nYou can also specify the path to a configuration file by using the \"--config\" option if\nyou want to override the default or the local settings.\n\nFor each check, three values are possible:\n* `\"disabled\"`: the check is not performed.\n* `\"enabled\"`: the check is performed.\n* `\"skip-unittest\"`: the check is performed but not in the unit tests.\n\nAny other value deactivates a check.\n\nNote that the \"--skipTests\" option is the equivalent of changing each\n`\"enabled\"` check by a `\"skip-unittest\"` check.\n\n#### Implemented checks\n* Old alias syntax (i.e \"alias a b;\" should be replaced with \"alias b = a;\").\n* Implicit concatenation of string literals.\n* Complex number literals (e.g. \"1.23i\").\n* Empty declarations (i.e. random \";\" characters).\n* enum array literals in struct/class bodies.\n* Avoid Pokémon exception handling.\n* opCmp or opEquals, or toHash not declared \"const\".\n* Format numbers for readability.\n* *delete* keyword is deprecated.\n* \"fish operators\" (floating point operators) are deprecated.\n* Left side of a *foreach* or *foreach\\_reverse* range expression is larger than the right.\n* Left side of a slice expression is larger than the right.\n* Variable, struct, class, union, module, package, and interface names that do not comply with Phobos style guidelines.\n* Struct constructors that have a single parameter that has a default argument.\n* Assign expressions where the left side of the '=' operator is the same as the right.\n* 'if' statements where the 'else' block is the same as the 'if' block.\n* ||, \u0026\u0026, and == expressions where the left and right sides of the operator are identical.\n* \u0026\u0026 and || expressions where the order of operations is confusing.\n* Unused variables.\n* Unused parameters (check is skipped if function is marked \"override\").\n* Duplicate attributes.\n* Declaring opEquals without toHash.\n* Undocumented public declarations.\n* Subtraction from .length properties. (These may be unsigned and could lead to integer underflow)\n* Class, struct, and union member variables whose names conflict with built-in type properties.\n* Confusing asm syntax.\n* Placement of const, immutable, or inout before a function return type instead of after the parameters.\n* Functions in interface declarations redundantly marked 'abstract'.\n* Declaring a variable with the same name as a label.\n* Variables that could have been declared const or immutable (experimental)\n* Redundant parenthesis.\n* Unused labels.\n* Lines longer than `max_line_length` characters.\n* Incorrect infinite range definitions.\n* Some assertions that check conditions that will always be true.\n* Auto functions without return statement. The compiler doesn't see an omission and it infers 'void' as return type.\n* `final` attribute is used but in this context it's a noop.\n* Check for properly documented public functions (\"Returns\" and \"Params\" sections). Initially implemented to lint Phobos. By default disabled.\n* Check for explicitly annotated unittests (_@system_ or _@safe_). Initially implemented to lint Phobos. By default disabled.\n* Check for that imports are sorted. Initially implemented to lint Phobos. By default disabled.\n* Virtual calls inside classes constructors.\n* Useless initializers.\n* Allman brace style\n* Redundant visibility attributes\n* Public declarations without a documented unittest. By default disabled.\n* Asserts without an explanatory message. By default disabled.\n* Indentation of if constraints\n* Check that `@trusted` is not applied to a whole scope. Trusting a whole scope can be a problem when new declarations are added and if they are not verified manually to be trustable.\n* Redundant storage class attributes\n* Cyclomatic complexity threshold per function and unittest (starts at 1, increased by 1 at each `if`, switch `case`, loop, `\u0026\u0026`, `||`, `?:`, `throw`, `catch`, `return`, `break`, `continue`, `goto` and function literal)\n\n#### Wishlist\n\n[See this list of open issues](https://github.com/Hackerpilot/Dscanner/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement) for the wishlist.\n\n### Reports\nThe \"--report\" option writes a JSON report on the static analysis checks\ndocument above to standard output. This file is usually used by the D plugin for\nSonarQube located [here](https://github.com/economicmodeling/sonar-d-plugin).\n\nUsing option \"--reportFormat sonarQubeGenericIssueData\" a report in a sonar-scanner\nsupported [Generic Issue Data format](https://docs.sonarqube.org/latest/analysis/generic-issue/) can be created.\n\n    $ dscanner --reportFormat sonarQubeGenericIssueData . \u003e sonar-generic-issue-data.json\n\nReference the report filename in sonar-project.properties using key \"sonar.externalIssuesReportPaths\"\n\n    sonar.externalIssuesReportPaths=sonar-generic-issue-data.json\n\n### Find Declaration\nAck, grep, and The Silver Searcher are useful for finding usages of symbols, but\ntheir signal to noise ratio is not very good when searching for a symbol's\ndeclaration. The \"--declaration\" or \"-d\" options allow you to search for a\nsymbols declaration. For example:\n\n\t$ dscanner -d TokenStructure\n\t./libdparse/src/std/lexer.d(248:8)\n\n### Line of Code Count\nThe \"--sloc\" or \"-l\" option prints the number of lines of code in the file.\nInstead of simply printing the number of line breaks, this counts the number of\nsemicolon, while, if, do, else, switch, for, foreach, foreach\\_reverse, default,\nand case tokens in the file.\n\n\t$ ./dscanner --sloc helloworld.d\n\t2\n\n### Syntax Highlighting\nThe \"--highlight\" option prints the given source file as syntax-highlighted HTML\nto the standard output. The CSS styling uses the [Solarized](http://ethanschoonover.com/solarized)\ncolor scheme by default, but can be customised using the \"--theme\" option.\n\nThe following themes are available:\n\n- `solarized`\n- `solarized-dark`\n- `gruvbox`\n- `gruvbox-dark`\n\n\tNo example. It would take up too much space\n\n### CTAGS Output\nThe \"--ctags\" or \"-c\" option generates CTAGS information and writes it to the\nstandard output. Directory arguments are scanned recursively for `.d` and `.di`\nfiles.\n\n\t$ dscanner --ctags helloworld.d\n\t!_TAG_FILE_FORMAT\t2\n\t!_TAG_FILE_SORTED\t1\n\t!_TAG_FILE_AUTHOR\tBrian Schott\n\t!_TAG_PROGRAM_URL\thttps://github.com/Hackerpilot/Dscanner/\n\tmain\thelloworld.d\t3;\"\tf\tarity:1\n\nCTAGS output uses the following tag kinds:\n\n* g -- enum declarataion\n* e -- enum member\n* v -- variable declaration\n* i -- interface declaration\n* c -- class declaration\n* s -- struct declaration\n* f -- function declaration\n* u -- union declaration\n* T -- template declaration\n* a -- alias declarataion\n\nMore information on the CTAGS format can be found [here](http://ctags.sourceforge.net/FORMAT).\n\n### Etags Output\nThe `--etags`, `-e`, and `--etagsAll` options are similar to `--ctags` except\nthat an Emacs-compatible tags file is generated. The `--etagsAll` option\ngenerates tags for private and package declarations in addition to what\n`--etags` and `-e` generate.\n\n### Outline\nThe \"--outline\" option parses the given D source file and writes an simple\noutline of the file's declarations to stdout.\n\n### Configuration\n\nIf a `dscanner.ini` file is locate in the working directory or any of it's\nparents, it overrides any other configuration files.\n\nAs final location, D-Scanner uses the configuration file given in\n`$HOME/.config/dscanner/dscanner.ini`. Run `--defaultConfig` to regenerate it.\n\nThe `--config` option allows one to use a custom configuration file path.\n\n### AST Dump\nThe \"--ast\" or \"--xml\" options will dump the complete abstract syntax tree of\nthe given source file to standard output in XML format.\n\n```sh\n$ dscanner --ast helloworld.d\n```\n\n```xml\n\u003cmodule\u003e\n\u003cdeclaration\u003e\n\u003cimportDeclaration\u003e\n\u003csingleImport\u003e\n\u003cidentifierChain\u003e\n\u003cidentifier\u003estd\u003c/identifier\u003e\n\u003cidentifier\u003estdio\u003c/identifier\u003e\n\u003c/identifierChain\u003e\n\u003c/singleImport\u003e\n\u003c/importDeclaration\u003e\n\u003c/declaration\u003e\n\u003cdeclaration\u003e\n\u003cfunctionDeclaration line=\"3\"\u003e\n\u003cname\u003emain\u003c/name\u003e\n\u003ctype pretty=\"void\"\u003e\n\u003ctype2\u003e\nvoid\n\u003c/type2\u003e\n\u003c/type\u003e\n\u003cparameters\u003e\n\u003cparameter\u003e\n\u003cname\u003eargs\u003c/name\u003e\n\u003ctype pretty=\"string[]\"\u003e\n\u003ctype2\u003e\n\u003csymbol\u003e\n\u003cidentifierOrTemplateChain\u003e\n\u003cidentifierOrTemplateInstance\u003e\n\u003cidentifier\u003estring\u003c/identifier\u003e\n\u003c/identifierOrTemplateInstance\u003e\n\u003c/identifierOrTemplateChain\u003e\n\u003c/symbol\u003e\n\u003c/type2\u003e\n\u003ctypeSuffix type=\"[]\"/\u003e\n\u003c/type\u003e\n\u003cidentifier\u003eargs\u003c/identifier\u003e\n\u003c/parameter\u003e\n\u003c/parameters\u003e\n\u003cfunctionBody\u003e\n\u003cblockStatement\u003e\n\u003cdeclarationsAndStatements\u003e\n\u003cdeclarationOrStatement\u003e\n\u003cstatement\u003e\n\u003cstatementNoCaseNoDefault\u003e\n\u003cexpressionStatement\u003e\n\u003cexpression\u003e\n\u003cassignExpression\u003e\n\u003cfunctionCallExpression\u003e\n\u003cunaryExpression\u003e\n\u003cprimaryExpression\u003e\n\u003cidentifierOrTemplateInstance\u003e\n\u003cidentifier\u003ewriteln\u003c/identifier\u003e\n\u003c/identifierOrTemplateInstance\u003e\n\u003c/primaryExpression\u003e\n\u003c/unaryExpression\u003e\n\u003carguments\u003e\n\u003cargumentList\u003e\n\u003cassignExpression\u003e\n\u003cprimaryExpression\u003e\n\u003cstringLiteral\u003eHello World\u003c/stringLiteral\u003e\n\u003c/primaryExpression\u003e\n\u003c/assignExpression\u003e\n\u003c/argumentList\u003e\n\u003c/arguments\u003e\n\u003c/functionCallExpression\u003e\n\u003c/assignExpression\u003e\n\u003c/expression\u003e\n\u003c/expressionStatement\u003e\n\u003c/statementNoCaseNoDefault\u003e\n\u003c/statement\u003e\n\u003c/declarationOrStatement\u003e\n\u003c/declarationsAndStatements\u003e\n\u003c/blockStatement\u003e\n\u003c/functionBody\u003e\n\u003c/functionDeclaration\u003e\n\u003c/declaration\u003e\n\u003c/module\u003e\n```\n\nFor more readable output, pipe the command through [xmllint](http://xmlsoft.org/xmllint.html)\nusing its formatting switch.\n\n\t$ dscanner --ast helloworld.d | xmllint --format -\n\nSelecting modules for a specific check\n--------------------------------------\n\nIt is possible to create a new section `analysis.config.ModuleFilters` in the `dscanner.ini`.\nIn this optional section a comma-separated list of inclusion and exclusion selectors can\nbe specified for every check on which selective filtering should be applied.\nThese given selectors match on the module name and partial matches (`std.` or `.foo.`) are possible.\nMoreover, every selectors must begin with either `+` (inclusion) or `-` (exclusion).\nExclusion selectors take precedence over all inclusion operators.\nOf course, for every check a different selector set can given:\n\n```ini\n[analysis.config.ModuleFilters]\nfinal_attribute_check = \"+std.foo,+std.bar\"\nuseless_initializer = \"-std.\"\n```\n\nA few examples:\n\n- `+std.`: Includes all modules matching `std.`\n- `+std.bitmanip,+std.json`: Applies the check only for these two modules\n- `-std.bitmanip,-std.json`: Applies the check for all modules, but these two\n- `+.bar`: Includes all modules matching `.bar` (e.g. `foo.bar`, `a.b.c.barros`)\n- `-etc.`: Excludes all modules from `.etc`\n- `+std,-std.internal`: Includes entire `std`, except for the internal modules\n","funding_links":[],"categories":["Dev Tools","Programming Languages"],"sub_categories":["Bare metal / kernel development"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdlang-community%2FD-Scanner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdlang-community%2FD-Scanner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdlang-community%2FD-Scanner/lists"}