icsharpcode / NRefactory

NRefactory - Refactoring Your C# Code
684 stars 264 forks source link

Note: There is currently no maintainer for NRefactory.

If you need a C# parser / compiler frontend, use Microsoft.CodeAnalysis (Roslyn) instead.

The refactorings in NRefactory have been ported to Roslyn: https://github.com/icsharpcode/RefactoringEssentials/


Overview of the NRefactory library:

Introductory documentation: http://www.codeproject.com/Articles/408663/Using-NRefactory-for-analyzing-Csharp-code

How to download:

How to compile:

  1. Get Mono.Cecil 0.9.6 Get Cecil from https://github.com/jbevain/cecil ('git clone git://github.com/jbevain/cecil.git') or download Cecil from https://github.com/jbevain/cecil/ and unzip it into a directory named "cecil" next to the directory containing NRefactory.
  2. If you need the IKVM binding get it from https://github.com/mono/ikvm-fork and put it next to the NRefactory directory like Mono.Cecil - name it "ikvm".
  3. Open NRefactory.sln in your favorite .NET IDE and compile.

Features:

Non-Features:

Dependencies: .NET 4.0 Mono.Cecil 0.9.6 NRefactory contains a modified copy of mcs (Mono's C# compiler) for the C# parser.

Namespace overview:

ICSharpCode.NRefactory assembly ICSharpCode.NRefactory.Editor: Interfaces that abstract from the text editor control used. Maybe future AvalonEdit versions will directly implement these interfaces, but you could also write adapters for other editors.

ICSharpCode.NRefactory.TypeSystem:
    Contains a language-independent representation of the .NET type system.
    The type system is divided into two portions: unresolved and resolved type systems.

ICSharpCode.NRefactory.TypeSystem.Implementation:
    Contains default implementations for the type system interfaces.

ICSharpCode.NRefactory.Semantics:
    Contains classes (ResolveResults) used to represent the semantics of a language element.
    ResolveResults can have child results, thus forming semantic trees.

ICSharpCode.NRefactory.Documentation:
    Classes for working with .xml documentation files.
    See "doc/XML Documentation.html" for details.

ICSharpCode.NRefactory.PatternMatching:
    Provides classes for pattern matching over the C# and VB ASTs.
    See "doc/Pattern Matching.html" for details.

ICSharpCode.NRefactory.Util:
    Various helper classes.

ICSharpCode.NRefactory.CSharp assembly ICSharpCode.NRefactory.CSharp: Abstract Syntax Tree for C#

ICSharpCode.NRefactory.CSharp.Completion:
    Code completion (IntelliSense) for C#

ICSharpCode.NRefactory.CSharp.Resolver:
    Semantic analysis for C# (resolving the meaning of expressions)

ICSharpCode.NRefactory.CSharp.Analysis:
    Semantic analysis for C# (additional analysis functionality)

ICSharpCode.NRefactory.CSharp.TypeSystem:
    Contains type system implementations specific to C#.

ICSharpCode.NRefactory.CSharp.Refactoring:
    Infrastructure for refactorings; and several built-in refactorings.

ICSharpCode.NRefactory.VB assembly ICSharpCode.NRefactory.VB: Abstract Syntax Tree for VB

ICSharpCode.NRefactory.Xml assembly ICSharpCode.NRefactory.Xml: Error-tolerant XML parser. Supports incremental parsing. When tags don't match correctly, the parser uses a heuristic to guess the parse tree. The heuristic tries to minimize the edit distance from the erroneous document to the fixed document, and it also considers the indentation.

Null-Object pattern: The NRefactory library makes extensive use of the null object pattern. As a result, NullReferenceExceptions should be very rare when working with this library. In the type system, both ITypeReference and IType use SpecialType.UnknownType to represent unknown types. Unless the XML documentation says otherwise, no method or property returning a ITypeReference or IType will return null.

Note that the null object pattern is not used for ITypeDefinition:
IProjectContent.GetClass() returns null when a type is not found. Take care to abort your
operation or substitute UnknownType instead of passing the null to code expecting an IType.

The pattern also extends to the C# resolver, which always produces a ResolveResult, even in
error cases. Use ResolveResult.IsError to detect resolver errors.
Also note that many resolver errors still have a meaningful type attached, this allows code
completion to work in the presence of minor semantic errors.

The C# AST makes use of special null nodes when accessing the getter of an AST property and no
child node with that role exists. Check the IsNull property to test whether a node is a null node.
Null nodes are not considered to be part of the AST (e.g. they don't have a parent).

FAQ: Q: What is the difference between NRefactory and Roslyn?

Q: What is the difference between types and type definitions?

A: Basically, a type (IType) is any type in the .NET type system:

Q: What is the difference between types and type references? I've seen lots of duplicated classes (ArrayType vs. ArrayTypeReference, etc.)

A: If you've previously used the .NET Reflection API, the concept of type references will be new to you.

NRefactory has the concept of the "unresolved type system": every assembly/project is stored
in unresolved form.
It is possible to load some source code into a project which contains the type reference
"int[]" without having to load mscorlib into NRefactory.
So inside the entities stored for the project, the array type is only referenced using an
ITypeReference.
This interface has a single method:
    interface ITypeReference {
       IType Resolve(ITypeResolutionContext context);
    }
By calling the Resolve()-method, you will get back the actual ArrayType.

Not only type references are split up this way; pretty much every class in the type system
comes in unresolved and resolved forms.

Q: What is a compilation (ICompilation)?

A: A compilation serves as the root object of the resolved type system. It consists of a main assembly and multiple referenced assemblies, and may also contain additional information like compiler options.

Q: What's in an ITypeResolveContext?

A: An ITypeResolveContext is an environment for looking up namespaces and types. It consists of the current compilation, the current type definition, and language-specific implementations may add additional information (e.g. the list of usings).

Generally, you cannot resolve a type reference without knowing what kind of context is expected.

Q: How do I get the IType or ITypeReference for a primitive type such as string or int?

A: To get an IType for a primitive type, use: compilation.FindType(KnownTypeCode.Int32)

To get an ITypeReference, use:
    KnownTypeReference.Int32

It is also possible to use a System.Type for retrieving a type:
   compilation.FindType(typeof(int))
or
   typeof(int).ToTypeReference().

Q: Is it thread-safe?

A: This question is a bit difficult to answer. NRefactory was designed to be usable in a multi-threaded IDE. But of course, this does not mean that everything is thread-safe.

First off, there's no hidden static state, so any two operations working on independent data
can be executed concurrently.
[Actually, sometimes static state is used for caches, but those uses are thread-safe.]
TODO: what about the C# parser? gmcs is full of static state...
    -> this is being worked on; for the time being, NRefactory uses a global lock during parsing;
       so it's thread-safe but slow.

Some instance methods may use hidden instance state, so it is not safe to e.g. use an instance
of the CSharp.Resolver.TypeInference class concurrently.
Instead, you need to create an instance on every thread.

The type system itself is thread-safe. Both the unresolved and the resolved type systems are
immutable.

When you add to an IProjectContent, the existing project content isn't modified,
but a new project content is created instead. All implementations of the type system interfaces
are either immutable or freezable.

And on the resolved type system side, ICompilation is immutable as well.
Internally, the resolved type system will resolve elements only on demand, but this is done
in a thread-safe manner.

Q: What format do the .ToString() methods use?

A: They don't use any particular format. They're merely intended as a debugging aid. Currently .ToString() usually matches .ReflectionName, but that may change in the future.