Types declared in the same namespace, should have their source files stored in the same directory
The namespaces hierarchy should be synchronized with the source files directory hierarchy — a widely accepted convention; misalignment makes code less maintainable and harder to navigate. Hence types in the same namespace should have their source files in the same directory.
For each namespace with source files in several directories, the main directory is inferred (preferably the one named like the namespace). The rule matches the namespace, typesDeclaredOutOfMainDir (source files outside the main directory), the main directory, and typesDeclaredInMainDir (informational).
Source files out of the project directory are ignored: such linked files are shared across projects and already matched by the rule Avoid referencing source file out of the project directory.
For each namespace with source files in several directories, the main directory is inferred (preferably the one named like the namespace). The rule matches the namespace, typesDeclaredOutOfMainDir (source files outside the main directory), the main directory, and typesDeclaredInMainDir (informational).
Source files out of the project directory are ignored: such linked files are shared across projects and already matched by the rule Avoid referencing source file out of the project directory.
How to fix
Violations are the types in typesDeclaredOutOfMainDir. Typically such type…
- … is in the wrong namespace, right directory: move it to the namespace corresponding to the parent directory.
- … is in the right namespace, wrong directory: move its source file to the main directory.
- … is declared in source files in different directories: prefer a single directory.
Estimated Debt: 2 minutes plus 5 minutes per type in typesDeclaredOutOfMainDir.
- … is in the wrong namespace, right directory: move it to the namespace corresponding to the parent directory.
- … is in the right namespace, wrong directory: move its source file to the main directory.
- … is declared in source files in different directories: prefer a single directory.
Estimated Debt: 2 minutes plus 5 minutes per type in typesDeclaredOutOfMainDir.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Types declared in the same namespace, should have their source files stored in the same directory</Name>
// <Id>ND2105:TypesDeclaredInTheSameNamespaceShouldHaveTheirSourceFilesStoredInTheSameDirectory</Id>
warnif count > 0
from @namespace in Application.Namespaces
// Group types of @namespace in a lookup
// where groups are keyed with directories that contain the types' source file(s).
// Note that a type can be contained in several groups
// if it is declared in several source files stored in different directories.
let lookup = @namespace.ChildTypes.Where(
t => t.SourceFileDeclAvailable &&
JustMyCode.Contains(t) &&
// Don't match ASP.NET application types declared in Global.asax file
// that typically are in the root directory of the VS project.
!string.Equals(
t.SourceDecls.First().SourceFile.FileName,
"global.asax",
StringComparison.OrdinalIgnoreCase))
.ToMultiKeyLookup(
t => t.SourceDecls
// Source files out of the project directory are linked files shared across
// projects: they follow no single project's folder layout (ND2100 matches them)
.Where(decl => t.ParentAssembly.VisualStudioProjectFilePath == null ||
decl.SourceFile.FilePath.IsChildOf(t.ParentAssembly.VisualStudioProjectFilePath.ParentDirectoryPath))
.Select(decl => decl.SourceFile.FilePath.ParentDirectoryPath).Distinct()
)
// Are types of @namespaces declared in more than one directory?
where lookup.Count > 1
// Infer the main folder, preferably the one that has the same name as the namespace.
let dirs = lookup.Select(types => types.Key)
let mainDirNullable = dirs.Where(d => d.DirectoryName == @namespace.SimpleName).FirstOrDefault()
let mainDir = mainDirNullable ?? dirs.First()
// Types declared out of mainDir, are types in group of types declared in a directory different than mainDir!
let typesDeclaredOutOfMainDir =
lookup.Where(types => types.Key != mainDir)
.SelectMany(types => types)
// Filter types declared on several source files that contain generated methods
// because typically such type contains one or several partial definitions generated.
// These partially generated types would be false positive for the present rule.
.Where(t => t.SourceDecls.Count() == 1 ||
t.Methods.Count(m => JustMyCode.Contains(m)) == 0)
where typesDeclaredOutOfMainDir.Count() > 0 &&
// Don't warn for types with at least one declaration in a generated file
// like the UWP generated file 'App.g.i.cs'
!typesDeclaredOutOfMainDir
.Where(t => t.SourceFileDeclAvailable)
.SelectMany(t => t.SourceDecls.Select(sd => sd.SourceFile.FilePathString))
.Any(fps => fps.IndexOf(".g.", StringComparison.OrdinalIgnoreCase) >= 0)
let typesDeclaredInMainDir =
lookup.Where(types => types.Key == mainDir)
.SelectMany(types => types)
select new {
@namespace,
typesDeclaredOutOfMainDir,
mainDir = mainDir.ToString(),
typesDeclaredInMainDir,
Debt = (2+5*typesDeclaredOutOfMainDir.Count()).ToMinutes().ToDebt(),
Severity = Severity.High
}
//<Expl>
// The *{0}* declarations are present in source files spread across multiple directories.
// *{1}* {1?is:are} declared in source files out of *{2}*.
//</Expl>
//<Desc>
// The namespaces hierarchy should be synchronized with the source files
// directory hierarchy — a widely accepted convention; misalignment makes
// code less maintainable and harder to navigate. Hence types in the same
// namespace should have their source files in the same directory.
//
// For each namespace with source files in several directories, the **main
// directory** is inferred (preferably the one named like the namespace).
// The rule matches the namespace, **typesDeclaredOutOfMainDir** (source
// files outside the *main directory*), the *main directory*, and
// *typesDeclaredInMainDir* (informational).
//
// Source files out of the project directory are ignored: such linked files
// are shared across projects and already matched by the rule *Avoid
// referencing source file out of the project directory*.
//</Desc>
//<HowToFix>
// Violations are the types in **typesDeclaredOutOfMainDir**. Typically such
// type…
//
// - … is in the wrong namespace, right directory: move it to the namespace
// corresponding to the parent directory.
//
// - … is in the right namespace, wrong directory: move its source file to
// the *main directory*.
//
// - … is declared in source files in different directories: prefer a
// single directory.
//
// Estimated Debt: 2 minutes plus 5 minutes per type in
// *typesDeclaredOutOfMainDir*.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Source Files Organization: all 6 items
- Avoid referencing source file out of the project directory ND2100
- Avoid duplicating a type definition across assemblies ND2101
- Avoid defining multiple types in a source file ND2102
- Namespace name should correspond to file location ND2103
- Types with source files stored in the same directory, should be declared in the same namespace ND2104
- Types declared in the same namespace, should have their source files stored in the same directory ND2105