Open-source project
meziantou/Meziantou.Analyzer avatar
meziantou/Meziantou.Analyzer

Meziantou.Analyzer: a Roslyn analyzer set where the rule table tells you which fixes are automatable

A powerful C# Roslyn analyzer that uses static analysis to detect bugs, surface security issues, and enforce best practices—helping developers and AI write more reliable code.

1,214 stars72 forksC#MIT

At a glance

What is it?
A single NuGet package of C# analyzers covering usage, style, performance, security and design, most with a code fix, several configurable, and one documented as deprecated in favour of a built-in rule.
Who is it for?
Meziantou.Analyzer is best read as a supplement to the built-in .NET analyzer set rather than a replacement for it, and the repository is candid enough to say so in its own rule table. The MA0018 row, marked deprecated and pointing at CA1000, is the clearest signal: where the platform ships a good rule, this package gets out of the way.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One package, two ways to attach it to a solution

Meziantou.Analyzer is a Roslyn analyzer package rather than a standalone tool, so there is no daemon to configure and no separate ruleset file to wire into your build. You add a package reference and the compiler loads the analyzers:

bash
dotnet package add Meziantou.Analyzer

The second install path is the one worth planning around. If your repository uses Central Package Management, the README shows how to declare the analyzer as a `GlobalPackageReference` in `Directory.Packages.props`:

xml
<ItemGroup>
  <GlobalPackageReference Include="Meziantou.Analyzer" Version="..." />
</ItemGroup>

That applies the analyzer to every project in the repository without touching a single project file, which matters for large solutions where adding a package reference to forty projects by hand is exactly the kind of chore that never gets done. The trade is that you inherit the rules everywhere, including test projects and generated code, so the plan for suppressions has to exist before the rollout rather than after the first noisy build.

The repository itself is organised the way you would expect from a project that builds and tests its own analyzers: `src/` and `tests/` at the root, shared MSBuild settings in `Directory.Build.props` and `Directory.Build.targets`, a pinned SDK through `global.json`, and dependency updates handled by `renovate.json`. The solution file uses the newer `.slnx` format, and both `AGENTS.md` and `CLAUDE.md` sit at the top level, which tells you the project expects automated agents to work in the tree and be given the same instructions a human contributor would read.

Reading the rule table as a design document

The README embeds a generated rules table with one row per rule and six columns that matter more than the descriptions do: Id, Category, Description, Severity, Is enabled, Code fix, Configurable. That last pair is the real information.

A code fix column means the analyzer can rewrite your code when you accept the suggestion, which changes the economics of adoption. A rule with a code fix can be enabled by default because accepting it is a keystroke. A rule without one generates a diagnostic you have to interpret and edit by hand, which is a much higher tax on the person reading the build log. MA0009, the rule that asks for a regex evaluation timeout, has no code fix and no configuration, because the right timeout depends on the pattern and only you know it. MA0016, which asks you to prefer a collection abstraction over its implementation, also has no code fix, since the correct abstraction is a design decision.

The configurable column separates rules with an opinion you might disagree with from rules that are close to unarguable. MA0004, use `Task.ConfigureAwait`, is configurable because some teams genuinely want to await on the captured context. MA0032, which asks you to pass a `CancellationToken` even when none is in scope, is configurable and, notably, not enabled by default. A package that ships a rule off by default is making an explicit statement that the rule is opinionated rather than a defect.

The categories are Usage, Style, Performance, Security and Design, which is a useful spread for a single package. Style rules are the ones teams argue about, so it is worth knowing that roughly a fifth of the visible table is Style before deciding the package is too noisy.

The security and defect rules are where the value sits

Two rules stand out because they address problems that reach production rather than problems that reach a code review comment.

MA0009 asks you to add a regex evaluation timeout and is filed under Security. A `Regex` constructed without a timeout is a denial-of-service risk in any service that accepts user input and matches it against a pattern, because a crafted string can drive backtracking to a state where the match never terminates. The built-in analyzer set does not cover this, and the fix is one constructor argument. MA0023 sits next to it, asking for `RegexOptions.ExplicitCapture` or named groups, which reduces the work the engine does per match and removes unnamed group indices from the code that has to stay in sync with the pattern.

Then there are the unfinished-work rules. MA0025 flags implementations that throw `NotImplementedException`, and MA0026 flags TODO comments. Both have no code fix and no configuration, which is consistent: there is no mechanical rewrite, and suppressing them is always a decision someone has to make deliberately.

The Design category has a similar character. MA0022 asks you to return `Task.FromResult` rather than returning null from an async-shaped method, MA0033 warns against tagging instance fields with `ThreadStaticAttribute`, since static storage for instance state is a data race waiting to happen, and MA0013 and MA0014 reject `ApplicationException` as an exception type. None of these need a profiler or a production incident to justify them.

The Performance rules are the most contestable of the set. MA0005 asks for `Array.Empty<T>()` over a new empty array, MA0029 asks you to combine chained LINQ calls, and MA0030 asks you to remove a useless `OrderBy`. Each is defensible in isolation and collectively they can push a codebase toward imperative code that is faster and harder to read, which is why several of them are configurable.

The package steers you toward built-in rules where they exist

MA0018 is described in the table as `Do not declare static members on generic types (deprecated; use CA1000 instead)`. That single row says something useful about how the project is positioned. It shipped a rule, found that the platform shipped a better version of the same rule under the `CA` prefix, and marked its own as deprecated instead of competing.

The README also links `docs/comparison-with-other-analyzers.md` as the place to check which rules duplicate well-known analyzers, which is the same gesture at the documentation level. Teams that already run the built-in .NET analyzers, StyleCop or SonarAnalyzer can use that document to see the overlap before adding the package, rather than discovering it as duplicate diagnostics on every build.

The MIT license and roughly 1,200 stars make this a low-friction dependency to try on a side branch. The open issue count sits at two, which for a project shipping releases on consecutive days suggests the issue tracker is not the bottleneck; questions probably land somewhere else.

One structural note. Individual rules link to their own pages under `docs/Rules/`, and `GenerateDocumentation.cmd` sits at the root, so the per-rule documentation is generated rather than written by hand. That is good for consistency, and it also means the rule table in the README is the authoritative artifact: it is regenerated from the same source as the site, so if the two ever disagree, the README is the one to trust.

A NuGet release train running at speed

The three most recent releases all landed within about two days in September 2026, and each one names a single change:

- `3.0.264` replaced the concurrent collections suggested by MA0234 with locked collections. - `3.0.263` added MA0240, do not use banned syntax, plus MA0241 for invalid entries. - `3.0.262` parallelized the steps of the `create_nuget` CI job.

Two observations. First, the rule numbering has reached at least MA0241, so the visible rows in the README are a small fraction of the package, and a rule set that size will produce more diagnostics than most teams want enabled on day one. Second, MA0240, do not use banned syntax, is a different shape from the rest: it is a policy mechanism, where you name syntax that is forbidden and the analyzer reports it. Combined with the configurable column on rules like MA0032, that suggests the package increasingly positions itself as a place to encode team conventions rather than only universal defects.

The fix to MA0234 is also worth noting as a maintenance signal. When a rule suggests a concurrent collection, using it correctly is easy to get wrong, so switching the suggestion to locked collections closes a class of misuse. It is the kind of change that only makes sense from someone who has seen the diagnostics in real code.

Because releases land this frequently, pinning the package version is the sensible default rather than floating on latest. The README's own snippet puts a literal `Version="..."` placeholder in the `GlobalPackageReference`, which is the right shape for a repository that will apply this package everywhere.

How to evaluate it without drowning in diagnostics

A reasonable first pass is to add the package to a single non-critical project, build, and sort the output by rule id rather than by severity. The Style rules will dominate the count and tell you little about whether the analyzer earns its place. Look instead at the Usage and Security rows, which is where MA0001 on a missing `StringComparison`, MA0011 on a missing `IFormatProvider`, and MA0009 on regex timeouts show up. Those are the diagnostics with a real cost when they are missed.

From there, two decisions matter. Whether to accept code fixes in bulk, which the table says is possible for most rules, and whether to suppress or fix the Performance rules as a group. Both are policy choices, and the configurable column exists so you can make them without forking the analyzer.

If the team is already running an AI coding assistant, the design intent here is worth taking seriously. The project description frames the analyzer as a safety net for AI-assisted workflows, and the presence of `AGENTS.md` and `CLAUDE.md` in the tree is consistent with that. The reasoning is sound: generated code follows the training distribution more than it follows your conventions, and an analyzer that flags missing `StringComparison` arguments or regex timeouts gives the agent a concrete signal to fix instead of a vague review comment. The caveat is that it also means agents will iterate against the diagnostics, so rules you would rather discuss in review are rules the agent will now silently satisfy. Choosing which rules are on is choosing what gets reviewed versus what gets automated.

Editorial conclusion

Meziantou.Analyzer is best read as a supplement to the built-in .NET analyzer set rather than a replacement for it, and the repository is candid enough to say so in its own rule table. The MA0018 row, marked deprecated and pointing at CA1000, is the clearest signal: where the platform ships a good rule, this package gets out of the way. What remains is the part the built-in set does not cover, from MA0009 on regex evaluation timeouts to MA0026 on TODO comments, and those are the rules worth turning on by default. The adoption path is cheap because the package installs as an analyzer reference and Central Package Management can apply it repository-wide through a `GlobalPackageReference`, but start by running it in warning mode on one project rather than flipping the severity table all at once. With rule numbers past MA0241 and NuGet releases landing on consecutive days, expect the table to keep changing, which makes `docs/Rules/` and the duplicated-rules comparison worth re-reading whenever you bump the version.

Frequently asked questions

Is Meziantou.Analyzer a replacement for the built-in .NET analyzers?

No, and the project says so in its own rule table. MA0018 is marked deprecated with a note to use CA1000 instead, and the README links `docs/comparison-with-other-analyzers.md` for checking which rules duplicate well-known analyzers. Treat it as a supplement that fills gaps the built-in set leaves, and read that comparison document before adding it to a solution that already runs the platform analyzers.

Which Meziantou.Analyzer rule should I enable first?

Start with the Security and Usage categories rather than Style, which produces the most output for the least value. MA0009 asks for a regex evaluation timeout and is one of the few rules here that addresses a defect that reaches production. MA0001 flags a missing `StringComparison` and MA0011 a missing `IFormatProvider`, both of which are culture-sensitive bugs that are easy to introduce and hard to notice.

Should I add Meziantou.Analyzer to my repository or to a single project?

Per project when you are evaluating, because the point of the first build is to see what your existing code trips on. If you use Central Package Management, the README shows adding it as a `GlobalPackageReference` in `Directory.Packages.props`, which applies the analyzer to every project in the repository. That is the better long-term shape, but it means test projects and generated code are analyzed too, so plan the suppression strategy first.

Official sources

  1. License: MIT
  2. meziantou/Meziantou.Analyzer on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/meziantou-meziantou-analyzer.svg)](https://hysenlabs.com/projects/meziantou-meziantou-analyzer)

Was this useful?