Skip to content

Commit

Permalink
Draft implementation of file-scoped-namespaces (#48937)
Browse files Browse the repository at this point in the history
Co-authored-by: Rikki Gibson <rigibson@microsoft.com>
  • Loading branch information
CyrusNajmabadi and RikkiGibson authored Jun 21, 2021
1 parent b8f45f6 commit 1a69700
Show file tree
Hide file tree
Showing 61 changed files with 3,222 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ private static void ComputeDeclarations(
switch (node.Kind())
{
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.SingleLineNamespaceDeclaration:
{
var ns = (NamespaceDeclarationSyntax)node;
var ns = (BaseNamespaceDeclarationSyntax)node;
foreach (var decl in ns.Members)
{
ComputeDeclarations(model, associatedSymbol: null, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ public override Binder VisitInterfaceDeclaration(InterfaceDeclarationSyntax node
public override Binder VisitRecordDeclaration(RecordDeclarationSyntax node)
=> VisitTypeDeclarationCore(node);

public override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent)
public sealed override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent)
{
if (!LookupPosition.IsInNamespaceDeclaration(_position, parent))
{
Expand All @@ -774,7 +774,22 @@ public override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax pare
return VisitNamespaceDeclaration(parent, _position, inBody, inUsing);
}

internal Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent, int position, bool inBody, bool inUsing)
public override Binder VisitSingleLineNamespaceDeclaration(SingleLineNamespaceDeclarationSyntax parent)
{
if (!LookupPosition.IsInNamespaceDeclaration(_position, parent))
{
return VisitCore(parent.Parent);
}

// Anywhere after the `;` is in the 'body' of this namespace.
bool inBody = _position >= parent.SemicolonToken.EndPosition;

bool inUsing = IsInUsing(parent);

return VisitNamespaceDeclaration(parent, _position, inBody, inUsing);
}

internal Binder VisitNamespaceDeclaration(BaseNamespaceDeclarationSyntax parent, int position, bool inBody, bool inUsing)
{
Debug.Assert(!inUsing || inBody, "inUsing => inBody");

Expand Down Expand Up @@ -840,7 +855,7 @@ private static Binder MakeNamespaceBinder(CSharpSyntaxNode node, NameSyntax name
NamespaceSymbol ns = ((NamespaceSymbol)container).GetNestedNamespace(name);
if ((object)ns == null) return outer;

if (node is NamespaceDeclarationSyntax namespaceDecl)
if (node is BaseNamespaceDeclarationSyntax namespaceDecl)
{
outer = AddInImportsBinders((SourceNamespaceSymbol)outer.Compilation.SourceModule.GetModuleNamespace(ns), namespaceDecl, outer, inUsing);
}
Expand Down Expand Up @@ -957,7 +972,7 @@ internal Binder VisitCompilationUnit(CompilationUnitSyntax compilationUnit, bool

private static Binder AddInImportsBinders(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool inUsing)
{
Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration));
Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.SingleLineNamespaceDeclaration);

if (inUsing)
{
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/BinderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,11 @@ internal Binder GetInNamespaceBinder(CSharpSyntaxNode unit)
switch (unit.Kind())
{
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.SingleLineNamespaceDeclaration:
{
BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate();
visitor.Initialize(0, null, null);
Binder result = visitor.VisitNamespaceDeclaration((NamespaceDeclarationSyntax)unit, unit.SpanStart, inBody: true, inUsing: false);
Binder result = visitor.VisitNamespaceDeclaration((BaseNamespaceDeclarationSyntax)unit, unit.SpanStart, inBody: true, inUsing: false);
_binderFactoryVisitorPool.Free(visitor);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private sealed class FromSyntax : WithExternAliasesBinder
internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next)
: base(next)
{
Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration));
Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.SingleLineNamespaceDeclaration);
_declaringSymbol = declaringSymbol;
_declarationSyntax = declarationSyntax;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private sealed class FromSyntax : WithExternAndUsingAliasesBinder
internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, WithUsingNamespacesAndTypesBinder next)
: base(next)
{
Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration));
Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.SingleLineNamespaceDeclaration);
_declaringSymbol = declaringSymbol;
_declarationSyntax = declarationSyntax;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ private sealed class FromSyntax : WithUsingNamespacesAndTypesBinder
internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool withImportChainEntry)
: base(next, withImportChainEntry)
{
Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration));
Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.SingleLineNamespaceDeclaration);
_declaringSymbol = declaringSymbol;
_declarationSyntax = declarationSyntax;
}
Expand Down
12 changes: 12 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,18 @@ public static Conversion ClassifyConversion(this SemanticModel? semanticModel, i
return csmodel?.GetDeclaredSymbol(declarationSyntax, cancellationToken);
}

/// <summary>
/// Given a namespace declaration syntax node, get the corresponding namespace symbol for
/// the declaration assembly.
/// </summary>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static INamespaceSymbol? GetDeclaredSymbol(this SemanticModel? semanticModel, SingleLineNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
{
var csmodel = semanticModel as CSharpSemanticModel;
return csmodel?.GetDeclaredSymbol(declarationSyntax, cancellationToken);
}

/// <summary>
/// Given a type declaration, get the corresponding type symbol.
/// </summary>
Expand Down
68 changes: 40 additions & 28 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -682,7 +682,7 @@
<value>'{0}': no suitable method found to override</value>
</data>
<data name="ERR_NamespaceUnexpected" xml:space="preserve">
<value>A namespace cannot directly contain members such as fields or methods</value>
<value>A namespace cannot directly contain members such as fields, methods or statements</value>
</data>
<data name="ERR_NoSuchMember" xml:space="preserve">
<value>'{0}' does not contain a definition for '{1}'</value>
Expand Down Expand Up @@ -6601,6 +6601,18 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_FunctionPointersCannotBeCalledWithNamedArguments" xml:space="preserve">
<value>A function pointer cannot be called with named arguments.</value>
</data>
<data name="IDS_SingleLineNamespace" xml:space="preserve">
<value>single-line namespace</value>
</data>
<data name="ERR_MultipleSingleLineNamespace" xml:space="preserve">
<value>Source file can only contain one single-line namespace declaration.</value>
</data>
<data name="ERR_SingleLineAndNormalNamespace" xml:space="preserve">
<value>Source file can not contain both single-line and normal namespace declarations.</value>
</data>
<data name="ERR_SingleLineNamespaceNotBeforeAllMembers" xml:space="preserve">
<value>Single-line namespace must precede all other members in a file.</value>
</data>
<data name="WRN_UnreadRecordParameter" xml:space="preserve">
<value>Parameter '{0}' is unread. Did you forget to use it to initialize the property with that name?</value>
</data>
Expand Down
11 changes: 11 additions & 0 deletions src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2887,6 +2887,15 @@ internal Conversion ClassifyConversionForCast(int position, ExpressionSyntax exp
/// <returns>The namespace symbol that was declared by the namespace declaration.</returns>
public abstract INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// Given a namespace declaration syntax node, get the corresponding namespace symbol for
/// the declaration assembly.
/// </summary>
/// <param name="declarationSyntax">The syntax node that declares a namespace.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The namespace symbol that was declared by the namespace declaration.</returns>
public abstract INamespaceSymbol GetDeclaredSymbol(SingleLineNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// Given a type declaration, get the corresponding type symbol.
/// </summary>
Expand Down Expand Up @@ -5033,6 +5042,8 @@ protected sealed override ISymbol GetDeclaredSymbolCore(SyntaxNode node, Cancell
return this.GetDeclaredSymbol((TupleElementSyntax)node, cancellationToken);
case SyntaxKind.NamespaceDeclaration:
return this.GetDeclaredSymbol((NamespaceDeclarationSyntax)node, cancellationToken);
case SyntaxKind.SingleLineNamespaceDeclaration:
return this.GetDeclaredSymbol((SingleLineNamespaceDeclarationSyntax)node, cancellationToken);
case SyntaxKind.Parameter:
return this.GetDeclaredSymbol((ParameterSyntax)node, cancellationToken);
case SyntaxKind.TypeParameter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,12 @@ private static BoundNode GetLowerBoundNode(ImmutableArray<BoundNode> boundNodes)
return null;
}

public override INamespaceSymbol GetDeclaredSymbol(SingleLineNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't defined namespace inside a member.
return null;
}

public override INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define type inside a member.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1330,21 +1330,23 @@ private bool IsRegularCSharp

#region "GetDeclaredSymbol overloads for MemberDeclarationSyntax and its subtypes"

/// <summary>
/// Given a namespace declaration syntax node, get the corresponding namespace symbol for the declaration
/// assembly.
/// </summary>
/// <param name="declarationSyntax">The syntax node that declares a namespace.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The namespace symbol that was declared by the namespace declaration.</returns>
/// <inheritdoc/>
public override INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declarationSyntax);

return GetDeclaredNamespace(declarationSyntax).GetPublicSymbol();
}

private NamespaceSymbol GetDeclaredNamespace(NamespaceDeclarationSyntax declarationSyntax)
/// <inheritdoc/>
public override INamespaceSymbol GetDeclaredSymbol(SingleLineNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default)
{
CheckSyntaxNode(declarationSyntax);

return GetDeclaredNamespace(declarationSyntax).GetPublicSymbol();
}

private NamespaceSymbol GetDeclaredNamespace(BaseNamespaceDeclarationSyntax declarationSyntax)
{
Debug.Assert(declarationSyntax != null);

Expand Down Expand Up @@ -1429,7 +1431,7 @@ private NamedTypeSymbol GetDeclaredNamedType(CSharpSyntaxNode declarationSyntax,

private NamespaceOrTypeSymbol GetDeclaredNamespaceOrType(CSharpSyntaxNode declarationSyntax)
{
var namespaceDeclarationSyntax = declarationSyntax as NamespaceDeclarationSyntax;
var namespaceDeclarationSyntax = declarationSyntax as BaseNamespaceDeclarationSyntax;
if (namespaceDeclarationSyntax != null)
{
return GetDeclaredNamespace(namespaceDeclarationSyntax);
Expand Down Expand Up @@ -2276,7 +2278,8 @@ private NamespaceOrTypeSymbol GetDeclaredTypeMemberContainer(CSharpSyntaxNode me
if (memberDeclaration.Parent.Kind() == SyntaxKind.CompilationUnit)
{
// top-level namespace:
if (memberDeclaration.Kind() == SyntaxKind.NamespaceDeclaration)
if (memberDeclaration.Kind() == SyntaxKind.NamespaceDeclaration ||
memberDeclaration.Kind() == SyntaxKind.SingleLineNamespaceDeclaration)
{
return _compilation.Assembly.GlobalNamespace;
}
Expand Down Expand Up @@ -2307,7 +2310,8 @@ private NamespaceOrTypeSymbol GetDeclaredTypeMemberContainer(CSharpSyntaxNode me
}

// a namespace or a type in an explicitly declared namespace:
if (memberDeclaration.Kind() == SyntaxKind.NamespaceDeclaration || SyntaxFacts.IsTypeDeclaration(memberDeclaration.Kind()))
if (memberDeclaration.Kind() is SyntaxKind.NamespaceDeclaration or SyntaxKind.SingleLineNamespaceDeclaration ||
SyntaxFacts.IsTypeDeclaration(memberDeclaration.Kind()))
{
return container;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ internal static DeclarationKind ToDeclarationKind(this SyntaxKind kind)
case SyntaxKind.ClassDeclaration: return DeclarationKind.Class;
case SyntaxKind.InterfaceDeclaration: return DeclarationKind.Interface;
case SyntaxKind.StructDeclaration: return DeclarationKind.Struct;
case SyntaxKind.NamespaceDeclaration: return DeclarationKind.Namespace;
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.SingleLineNamespaceDeclaration:
return DeclarationKind.Namespace;
case SyntaxKind.EnumDeclaration: return DeclarationKind.Enum;
case SyntaxKind.DelegateDeclaration: return DeclarationKind.Delegate;
case SyntaxKind.RecordDeclaration: return DeclarationKind.Record;
Expand Down
Loading

0 comments on commit 1a69700

Please sign in to comment.