diff --git a/.gitignore b/.gitignore index 25fac6ae83fb2..9ea10718c520f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ UnitTestResults.html *.tmp *.tmp_proj *.log +*.wrn *.vspscc *.vssscc .builds diff --git a/build/VSL.Imports.Closed.targets b/build/VSL.Imports.Closed.targets index 1376cc60e1d03..a853bdca6e337 100644 --- a/build/VSL.Imports.Closed.targets +++ b/build/VSL.Imports.Closed.targets @@ -1,4 +1,34 @@ + + + + + $(IntermediateOutputPath)$(TargetFileName).pcbm + + + + + + + + + + + + - + - - + + true + true + + + + + + + + + - $(CompileDependsOn);ApplyOptimizations - $(CleanDependsOn);CleanApplyOptimizations;CleanPhoneCopy + $(CleanDependsOn);CleanPhoneCopy - - true - true - $(CompileDependsOn);FakeSignAssembly - $(IntermediateOutputPath)$(TargetFileName).fakesign - - - - - - - - - $(CreateVsixContainerDependsOn);SignVsixInputs $(PrepareForRunDependsOn);SignVsix true - + diff --git a/docs/specs/PortablePdb-Metadata.md b/docs/specs/PortablePdb-Metadata.md index 93a0f14b287a8..3a4032ad6e03f 100644 --- a/docs/specs/PortablePdb-Metadata.md +++ b/docs/specs/PortablePdb-Metadata.md @@ -373,6 +373,19 @@ Structure: TODO: Bit ordering. +##### Root Namespace (VB compiler) +Parent: Module + +Kind: {58b2eab6-209f-4e4e-a22c-b2d0f910c782} + +Structure: + + Blob ::= namespace + +| terminal | encoding | description| +|:---------|:---------|:-----------| +| _namespace_ | UTF8 string | The root namespace. | + ##### Edit and Continue Local Slot Map (C# & VB compilers) Parent: MethodDef diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index e4f2cfb97d156..0f4cb36e60db3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2283,25 +2283,6 @@ internal override CommonPEModuleBuilder CreateModuleBuilder( CompilationTestData testData, DiagnosticBag diagnostics, CancellationToken cancellationToken) - { - return this.CreateModuleBuilder( - emitOptions, - manifestResources, - assemblySymbolMapper, - testData, - diagnostics, - ImmutableArray.Empty, - cancellationToken); - } - - internal CommonPEModuleBuilder CreateModuleBuilder( - EmitOptions emitOptions, - IEnumerable manifestResources, - Func assemblySymbolMapper, - CompilationTestData testData, - DiagnosticBag diagnostics, - ImmutableArray additionalTypes, - CancellationToken cancellationToken) { // Do not waste a slot in the submission chain for submissions that contain no executable code // (they may only contain #r directives, usings, etc.) @@ -2326,8 +2307,6 @@ internal CommonPEModuleBuilder CreateModuleBuilder( PEModuleBuilder moduleBeingBuilt; if (_options.OutputKind.IsNetModule()) { - Debug.Assert(additionalTypes.IsEmpty); - moduleBeingBuilt = new PENetModuleBuilder( (SourceModuleSymbol)SourceModule, emitOptions, @@ -2343,8 +2322,7 @@ internal CommonPEModuleBuilder CreateModuleBuilder( kind, moduleProps, manifestResources, - assemblySymbolMapper, - additionalTypes); + assemblySymbolMapper); } // testData is only passed when running tests. diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 1d72ca569db15..6962133bddabd 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -603,8 +603,9 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) AsyncStateMachine stateMachineType; - // In case of async lambdas, the method has already been uniquely named, so there is no need to - // produce a unique method ordinal for the corresponding state machine type, whose name includes the (unique) method name. + // Synthesized methods have no ordinal stored in custom debug information (only user-defined methods have ordinals). + // In case of async lambdas, which synthesize a state machine type during the following rewrite, the containing method has already been uniquely named, + // so there is no need to produce a unique method ordinal for the corresponding state machine type, whose name includes the (unique) containing method name. const int methodOrdinal = -1; BoundStatement bodyWithoutAsync = AsyncRewriter.Rewrite(methodWithBody.Body, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out stateMachineType); diff --git a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs index 0325c25a809fd..40274c76ca52a 100644 --- a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs +++ b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs @@ -47,8 +47,11 @@ internal MethodWithBody(MethodSymbol method, BoundStatement body, ImportChain im /// only need one wrapper to call it non-virtually. /// private Dictionary _wrappers; - - private readonly NamedTypeSymbol _type; + + /// + /// Type symbol being compiled, or null if we compile a synthesized type that doesn't have a symbol (e.g. PrivateImplementationDetails). + /// + private readonly NamedTypeSymbol _typeOpt; /// /// The builder for generating code, or null if not in emit phase. @@ -65,10 +68,10 @@ internal MethodWithBody(MethodSymbol method, BoundStatement body, ImportChain im public LambdaFrame staticLambdaFrame; - public TypeCompilationState(NamedTypeSymbol type, CSharpCompilation compilation, PEModuleBuilder moduleBuilderOpt) + public TypeCompilationState(NamedTypeSymbol typeOpt, CSharpCompilation compilation, PEModuleBuilder moduleBuilderOpt) { this.Compilation = compilation; - _type = type; + _typeOpt = typeOpt; this.ModuleBuilderOpt = moduleBuilderOpt; } @@ -80,9 +83,8 @@ public NamedTypeSymbol Type get { // NOTE: currently it can be null if only private implementation type methods are compiled - // TODO: is it used? if yes, make sure it is not accessed when type is not available; - Debug.Assert((object)_type != null); - return _type; + Debug.Assert((object)_typeOpt != null); + return _typeOpt; } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs index fea5886589b2a..0192c37f247d4 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs @@ -223,9 +223,8 @@ public PEAssemblyBuilder( OutputKind outputKind, ModulePropertiesForSerialization serializationProperties, IEnumerable manifestResources, - Func assemblySymbolMapper = null, - ImmutableArray additionalTypes = default(ImmutableArray)) - : base(sourceAssembly, emitOptions, outputKind, serializationProperties, manifestResources, assemblySymbolMapper, additionalTypes) + Func assemblySymbolMapper = null) + : base(sourceAssembly, emitOptions, outputKind, serializationProperties, manifestResources, assemblySymbolMapper, ImmutableArray.Empty) { } diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 0af79898f6805..849f11c5f86d9 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -51,6 +51,9 @@ ..\CodeAnalysisRules.ruleset + + MetadataReader\MetadataReaderPdbExtensions.cs + diff --git a/src/Compilers/Core/Portable/Diagnostic/Diagnostic_SimpleDiagnostic.cs b/src/Compilers/Core/Portable/Diagnostic/Diagnostic_SimpleDiagnostic.cs index 4d36f6c3db7c7..e5c370a9c0ff1 100644 --- a/src/Compilers/Core/Portable/Diagnostic/Diagnostic_SimpleDiagnostic.cs +++ b/src/Compilers/Core/Portable/Diagnostic/Diagnostic_SimpleDiagnostic.cs @@ -34,13 +34,18 @@ private SimpleDiagnostic( if ((warningLevel == 0 && severity != DiagnosticSeverity.Error) || (warningLevel != 0 && severity == DiagnosticSeverity.Error)) { - throw new ArgumentException("warningLevel"); + throw new ArgumentException(nameof(warningLevel)); + } + + if(descriptor == null) + { + throw new ArgumentNullException(nameof(descriptor)); } _descriptor = descriptor; _severity = severity; _warningLevel = warningLevel; - _location = location; + _location = location ?? Location.None; _additionalLocations = additionalLocations == null ? SpecializedCollections.EmptyReadOnlyList() : additionalLocations.ToImmutableArray(); _messageArgs = messageArgs ?? SpecializedCollections.EmptyArray(); } @@ -131,10 +136,9 @@ public override bool Equals(object obj) public override int GetHashCode() { return Hash.Combine(_descriptor, - Hash.Combine(_messageArgs.GetHashCode(), - Hash.Combine(_location.GetHashCode(), - Hash.Combine(_severity.GetHashCode(), _warningLevel) - ))); + Hash.CombineValues(_messageArgs, + Hash.Combine(_warningLevel, + Hash.Combine(_location, (int)_severity)))); } internal override Diagnostic WithLocation(Location location) diff --git a/src/Compilers/Core/Portable/InternalUtilities/Hash.cs b/src/Compilers/Core/Portable/InternalUtilities/Hash.cs index 4dfeff33829be..1bb82411f4323 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/Hash.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/Hash.cs @@ -66,6 +66,30 @@ internal static int CombineValues(IEnumerable values, int maxItemsToHash = return hashCode; } + internal static int CombineValues(T[] values, int maxItemsToHash = int.MaxValue) + { + if (values == null) + { + return 0; + } + + var maxSize = Math.Min(maxItemsToHash, values.Length); + var hashCode = 0; + + for (int i = 0; i < maxSize; i++) + { + T value = values[i]; + + // Should end up with a constrained virtual call to object.GetHashCode (i.e. avoid boxing where possible). + if (value != null) + { + hashCode = Hash.Combine(value.GetHashCode(), hashCode); + } + } + + return hashCode; + } + internal static int CombineValues(ImmutableArray values, int maxItemsToHash = int.MaxValue) { if (values.IsDefaultOrEmpty) diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs index ba6257b2e31a2..9899cc6c366c8 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; @@ -176,20 +177,6 @@ private void PopulateDebugTableRows() private const int ModuleImportScopeRid = 1; - // TODO: move to mdreader - private enum ImportScopeKind - { - ImportNamespace = 1, - ImportAssemblyNamespace = 2, - ImportType = 3, - ImportXmlNamespace = 4, - ImportAssemblyReferenceAlias = 5, - AliasAssemblyReference = 6, - AliasNamespace = 7, - AliasAssemblyNamespace = 8, - AliasType = 9 - } - private void SerializeImport(BinaryWriter writer, AssemblyReferenceAlias alias) { // ::= AliasAssemblyReference diff --git a/src/Diagnostics/FxCop/CSharp/CSharpFxCopRulesDiagnosticAnalyzers.csproj b/src/Diagnostics/FxCop/CSharp/CSharpFxCopRulesDiagnosticAnalyzers.csproj index adc536d2335ff..090116bd30e01 100644 --- a/src/Diagnostics/FxCop/CSharp/CSharpFxCopRulesDiagnosticAnalyzers.csproj +++ b/src/Diagnostics/FxCop/CSharp/CSharpFxCopRulesDiagnosticAnalyzers.csproj @@ -73,7 +73,6 @@ - @@ -107,4 +106,4 @@ - + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/CSharp/Design/CodeFixes/CA1001CSharpCodeFixProvider.cs b/src/Diagnostics/FxCop/CSharp/Design/CodeFixes/CA1001CSharpCodeFixProvider.cs deleted file mode 100644 index 0c115068b41e1..0000000000000 --- a/src/Diagnostics/FxCop/CSharp/Design/CodeFixes/CA1001CSharpCodeFixProvider.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Composition; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.FxCopAnalyzers.Design; -using Microsoft.CodeAnalysis.Simplification; - -namespace Microsoft.CodeAnalysis.CSharp.FxCopAnalyzers.Design -{ - /// - /// CA1001: Types that own disposable fields should be disposable - /// - [ExportCodeFixProvider(LanguageNames.CSharp, Name = CA1001DiagnosticAnalyzer.RuleId), Shared] - public class CA1001CSharpCodeFixProvider : CA1001CodeFixProviderBase - { - internal override Task GetUpdatedDocumentAsync(Document document, SemanticModel model, SyntaxNode root, SyntaxNode nodeToFix, Diagnostic diagnostic, CancellationToken cancellationToken) - { - //// We are going to implement IDisposable interface: - //// - //// public void Dispose() - //// { - //// throw new NotImplementedException(); - //// } - - var syntaxNode = nodeToFix as ClassDeclarationSyntax; - if (syntaxNode == null) - { - return Task.FromResult(document); - } - - var statement = CreateThrowNotImplementedStatement(model); - if (statement == null) - { - return Task.FromResult(document); - } - - var member = CreateSimpleMethodDeclaration(CA1001DiagnosticAnalyzer.Dispose, statement); - var newNode = - syntaxNode.BaseList != null ? - syntaxNode.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(CA1001DiagnosticAnalyzer.IDisposable))).AddMembers(new[] { member }) : - syntaxNode.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(CA1001DiagnosticAnalyzer.IDisposable))).AddMembers(new[] { member }).WithIdentifier(syntaxNode.Identifier.WithTrailingTrivia(SyntaxFactory.Space)); - newNode = newNode.WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation); - return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(nodeToFix, newNode))); - } - - protected StatementSyntax CreateThrowNotImplementedStatement(SemanticModel model) - { - var exceptionType = model.Compilation.GetTypeByMetadataName(NotImplementedExceptionName); - if (exceptionType == null) - { - // If we can't find the exception, we can't generate anything. - return null; - } - - return SyntaxFactory.ThrowStatement( - SyntaxFactory.ObjectCreationExpression( - SyntaxFactory.Token(SyntaxKind.NewKeyword), - SyntaxFactory.IdentifierName(exceptionType.Name), - SyntaxFactory.ArgumentList(), - null)); - } - - protected MethodDeclarationSyntax CreateSimpleMethodDeclaration(string name, StatementSyntax statement) - { - return SyntaxFactory.MethodDeclaration( - new SyntaxList(), - SyntaxFactory.TokenList(new SyntaxToken[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword) }), - SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), - null, - SyntaxFactory.Identifier(name), - null, - SyntaxFactory.ParameterList(), - new SyntaxList(), - SyntaxFactory.Block(statement), - new SyntaxToken()); - } - } -} diff --git a/src/Diagnostics/FxCop/Core/Design/CodeFixes/CA1001CodeFixProviderBase.cs b/src/Diagnostics/FxCop/Core/Design/CodeFixes/CA1001CodeFixProviderBase.cs deleted file mode 100644 index 620ef3d6b546e..0000000000000 --- a/src/Diagnostics/FxCop/Core/Design/CodeFixes/CA1001CodeFixProviderBase.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Immutable; - -namespace Microsoft.CodeAnalysis.FxCopAnalyzers.Design -{ - /// - /// CA1001: Types that own disposable fields should be disposable - /// - public abstract class CA1001CodeFixProviderBase : CodeFixProviderBase - { - protected const string NotImplementedExceptionName = "System.NotImplementedException"; - protected const string IDisposableName = "System.IDisposable"; - - public sealed override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(CA1001DiagnosticAnalyzer.RuleId); } - } - - protected sealed override string GetCodeFixDescription(Diagnostic diagnostic) - { - return FxCopFixersResources.ImplementIDisposableInterface; - } - } -} diff --git a/src/Diagnostics/FxCop/Core/FxCopRulesDiagnosticAnalyzers.csproj b/src/Diagnostics/FxCop/Core/FxCopRulesDiagnosticAnalyzers.csproj index d6e63e8122de2..36d58d9908f4f 100644 --- a/src/Diagnostics/FxCop/Core/FxCopRulesDiagnosticAnalyzers.csproj +++ b/src/Diagnostics/FxCop/Core/FxCopRulesDiagnosticAnalyzers.csproj @@ -103,7 +103,6 @@ - @@ -112,7 +111,6 @@ - @@ -195,4 +193,4 @@ - + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj new file mode 100644 index 0000000000000..0650326d5e7c5 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj @@ -0,0 +1,82 @@ + + + + + + + + + 12.0 + Debug + AnyCPU + {A36451EC-1127-40CE-B841-47F393D24624} + Library + true + System.Runtime.CSharp.Analyzers + System.Runtime.CSharp.Analyzers + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Profile7 + .NETPortable + true + + + + + + + + + {1ee8cad3-55f9-4d91-96b2-084641da9a6c} + CodeAnalysis + + + {b501a547-c911-4a05-ac6e-274a50dff30e} + CSharpCodeAnalysis + + + {5f8d2414-064a-4b3a-9b42-8e2a04246be5} + Workspaces + + + {baa0fee4-93c8-46f0-bb36-53a6053776c8} + SystemRuntimeAnalyzers + + + + + False + ..\..\..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/packages.config b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/packages.config new file mode 100644 index 0000000000000..7a5f834c82a3f --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/CodeFixProviderBase.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/CodeFixProviderBase.cs new file mode 100644 index 0000000000000..2e914bff0087e --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/CodeFixProviderBase.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; + +namespace System.Runtime.Analyzers +{ + public abstract class CodeFixProviderBase : CodeFixProvider + { + protected abstract string GetCodeFixDescription(Diagnostic diagnostic); + + internal abstract Task GetUpdatedDocumentAsync(Document document, SemanticModel model, SyntaxNode root, SyntaxNode nodeToFix, Diagnostic diagnostic, CancellationToken cancellationToken); + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var document = context.Document; + var cancellationToken = context.CancellationToken; + + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + foreach (var diagnostic in context.Diagnostics) + { + cancellationToken.ThrowIfCancellationRequested(); + + var nodeToFix = root.FindNode(diagnostic.Location.SourceSpan); + + var newDocument = await GetUpdatedDocumentAsync(document, model, root, nodeToFix, diagnostic, cancellationToken).ConfigureAwait(false); + + Debug.Assert(newDocument != null); + if (newDocument != document) + { + var codeFixDescription = GetCodeFixDescription(diagnostic); + context.RegisterCodeFix(new MyCodeAction(codeFixDescription, newDocument), diagnostic); + } + } + } + + private class MyCodeAction : DocumentChangeAction + { + public MyCodeAction(string title, Document newDocument) : + base(title, c => Task.FromResult(newDocument)) + { + } + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.Fixer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.Fixer.cs new file mode 100644 index 0000000000000..24cf7c5f0331f --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.Fixer.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; + +namespace System.Runtime.Analyzers +{ + /// + /// CA1001: Types that own disposable fields should be disposable + /// + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + public class TypesThatOwnDisposableFieldsShouldBeDisposableFixer : CodeFixProvider + { + protected const string NotImplementedExceptionName = "System.NotImplementedException"; + protected const string IDisposableName = "System.IDisposable"; + + public sealed override ImmutableArray FixableDiagnosticIds + { + get { return ImmutableArray.Create(TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer.RuleId); } + } + + protected string GetCodeFixDescription(Diagnostic diagnostic) + { + return SystemRuntimeAnalyzersResources.ImplementIDisposableInterface; + } + + public async override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var generator = SyntaxGenerator.GetGenerator(context.Document); + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + var declaration = root.FindNode(context.Span); + declaration = generator.GetDeclaration(declaration); + + if (declaration == null) + { + return; + } + + // We cannot have multiple overlapping diagnostics of this id. + var diagnostic = context.Diagnostics.Single(); + + context.RegisterCodeFix(new DocumentChangeAction(SystemRuntimeAnalyzersResources.ImplementIDisposableInterface, + async ct => await ImplementIDisposable(context.Document, declaration, ct).ConfigureAwait(false)), + diagnostic); + } + + private async Task ImplementIDisposable(Document document, SyntaxNode declaration, CancellationToken cancellationToken) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + var model = editor.SemanticModel; + + // Add the interface to the baselist. + var interfaceType = generator.TypeExpression(WellKnownTypes.IDisposable(model.Compilation)); + editor.AddInterfaceType(declaration, interfaceType); + + // Find a Dispose method. If one exists make that implement IDisposable, else generate a new method. + var typeSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol; + var disposeMethod = (typeSymbol?.GetMembers("Dispose"))?.OfType()?.Where(m => m.Parameters.Length == 0).FirstOrDefault(); + if (disposeMethod != null && disposeMethod.DeclaringSyntaxReferences.Length == 1) + { + var memberPartNode = await disposeMethod.DeclaringSyntaxReferences.Single().GetSyntaxAsync(cancellationToken).ConfigureAwait(false); + memberPartNode = generator.GetDeclaration(memberPartNode); + editor.ReplaceNode(memberPartNode, generator.AsPublicInterfaceImplementation(memberPartNode, interfaceType)); + } + else + { + var throwStatement = generator.ThrowStatement(generator.ObjectCreationExpression(WellKnownTypes.NotImplementedException(model.Compilation))); + var member = generator.MethodDeclaration(TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer.Dispose, statements: new[] { throwStatement }); + member = generator.AsPublicInterfaceImplementation(member, interfaceType); + editor.AddMember(declaration, member); + } + + return editor.GetChangedDocument(); + } + } +} diff --git a/src/Diagnostics/FxCop/Core/Design/CA1001DiagnosticAnalyzer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.cs similarity index 65% rename from src/Diagnostics/FxCop/Core/Design/CA1001DiagnosticAnalyzer.cs rename to src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.cs index be6c510698e20..0ac28edff1993 100644 --- a/src/Diagnostics/FxCop/Core/Design/CA1001DiagnosticAnalyzer.cs +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.cs @@ -1,34 +1,32 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.FxCopAnalyzers.Utilities; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.FxCopAnalyzers.Design +namespace System.Runtime.Analyzers { /// /// CA1001: Types that own disposable fields should be disposable /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - public sealed class CA1001DiagnosticAnalyzer : AbstractNamedTypeAnalyzer + public sealed class TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1001"; internal const string Dispose = "Dispose"; internal const string IDisposable = "System.IDisposable"; internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(RuleId, - new LocalizableResourceString(nameof(FxCopRulesResources.TypesThatOwnDisposableFieldsShouldBeDisposable), FxCopRulesResources.ResourceManager, typeof(FxCopRulesResources)), - new LocalizableResourceString(nameof(FxCopRulesResources.TypeOwnsDisposableFieldButIsNotDisposable), FxCopRulesResources.ResourceManager, typeof(FxCopRulesResources)), - FxCopDiagnosticCategory.Design, + new LocalizableResourceString(nameof(SystemRuntimeAnalyzersResources.TypesThatOwnDisposableFieldsShouldBeDisposable), SystemRuntimeAnalyzersResources.ResourceManager, typeof(SystemRuntimeAnalyzersResources)), + new LocalizableResourceString(nameof(SystemRuntimeAnalyzersResources.TypeOwnsDisposableFieldButIsNotDisposable), SystemRuntimeAnalyzersResources.ResourceManager, typeof(SystemRuntimeAnalyzersResources)), + DiagnosticCategory.Design, DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "http://msdn.microsoft.com/library/ms182172.aspx", - customTags: DiagnosticCustomTags.Microsoft); + customTags: WellKnownDiagnosticTags.Telemetry); public override ImmutableArray SupportedDiagnostics { @@ -38,7 +36,16 @@ public override ImmutableArray SupportedDiagnostics } } - protected override void AnalyzeSymbol(INamedTypeSymbol symbol, Compilation compilation, Action addDiagnostic, AnalyzerOptions options, CancellationToken cancellationToken) + public override void Initialize(AnalysisContext analysisContext) + { + analysisContext.RegisterSymbolAction(context => + { + AnalyzeSymbol((INamedTypeSymbol)context.Symbol, context.Compilation, context.ReportDiagnostic, context.Options, context.CancellationToken); + }, + SymbolKind.NamedType); + } + + private static void AnalyzeSymbol(INamedTypeSymbol symbol, Compilation compilation, Action addDiagnostic, AnalyzerOptions options, CancellationToken cancellationToken) { var disposableType = WellKnownTypes.IDisposable(compilation); if (disposableType != null && !symbol.AllInterfaces.Contains(disposableType)) diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DiagnosticCategory.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DiagnosticCategory.cs new file mode 100644 index 0000000000000..e67dfb9b94e99 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DiagnosticCategory.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace System.Runtime.Analyzers +{ + internal static class DiagnosticCategory + { + public static readonly string Design = SystemRuntimeAnalyzersResources.CategoryDesign; + public static readonly string Globalization = SystemRuntimeAnalyzersResources.CategoryGlobalization; + public static readonly string Interoperability = SystemRuntimeAnalyzersResources.CategoryInteroperability; + public static readonly string Performance = SystemRuntimeAnalyzersResources.CategoryPerformance; + public static readonly string Reliability = SystemRuntimeAnalyzersResources.CategoryReliability; + public static readonly string Usage = SystemRuntimeAnalyzersResources.CategoryUsage; + public static readonly string Naming = SystemRuntimeAnalyzersResources.CategoryNaming; + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DiagnosticExtensions.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DiagnosticExtensions.cs new file mode 100644 index 0000000000000..14385329d7868 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DiagnosticExtensions.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace System.Runtime.Analyzers +{ + internal static class DiagnosticExtensions + { + public static IEnumerable CreateDiagnostics( + this IEnumerable nodes, + DiagnosticDescriptor rule, + params object[] args) + { + foreach (var node in nodes) + { + yield return node.CreateDiagnostic(rule, args); + } + } + + public static Diagnostic CreateDiagnostic( + this SyntaxNode node, + DiagnosticDescriptor rule, + params object[] args) + { + return node.GetLocation().CreateDiagnostic(rule, args); + } + + public static IEnumerable CreateDiagnostics( + this IEnumerable tokens, + DiagnosticDescriptor rule, + params object[] args) + { + foreach (var token in tokens) + { + yield return token.CreateDiagnostic(rule, args); + } + } + + public static Diagnostic CreateDiagnostic( + this SyntaxToken token, + DiagnosticDescriptor rule, + params object[] args) + { + return token.GetLocation().CreateDiagnostic(rule, args); + } + + public static IEnumerable CreateDiagnostics( + this IEnumerable nodesOrTokens, + DiagnosticDescriptor rule, + params object[] args) + { + foreach (var nodeOrToken in nodesOrTokens) + { + yield return nodeOrToken.CreateDiagnostic(rule, args); + } + } + + public static Diagnostic CreateDiagnostic( + this SyntaxNodeOrToken nodeOrToken, + DiagnosticDescriptor rule, + params object[] args) + { + return nodeOrToken.GetLocation().CreateDiagnostic(rule, args); + } + + public static IEnumerable CreateDiagnostics( + this IEnumerable symbols, + DiagnosticDescriptor rule, + params object[] args) + { + foreach (var symbol in symbols) + { + yield return symbol.CreateDiagnostic(rule, args); + } + } + + public static Diagnostic CreateDiagnostic( + this ISymbol symbol, + DiagnosticDescriptor rule, + params object[] args) + { + return symbol.Locations.CreateDiagnostic(rule, args); + } + + public static IEnumerable CreateDiagnostics( + this IEnumerable locations, + DiagnosticDescriptor rule, + params object[] args) + { + foreach (var location in locations) + { + yield return location.CreateDiagnostic(rule, args); + } + } + + public static Diagnostic CreateDiagnostic( + this Location location, + DiagnosticDescriptor rule, + params object[] args) + { + if (!location.IsInSource) + { + return Diagnostic.Create(rule, null, args); + } + + return Diagnostic.Create(rule, location, args); + } + + public static IEnumerable CreateDiagnostics( + this IEnumerable> setOfLocations, + DiagnosticDescriptor rule, + params object[] args) + { + foreach (var locations in setOfLocations) + { + yield return locations.CreateDiagnostic(rule, args); + } + } + + public static Diagnostic CreateDiagnostic( + this IEnumerable locations, + DiagnosticDescriptor rule, + params object[] args) + { + var location = locations.First(l => l.IsInSource); + var additionalLocations = locations.Where(l => l.IsInSource).Skip(1); + return Diagnostic.Create(rule, + location: location, + additionalLocations: additionalLocations, + messageArgs: args); + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DocumentChangeAction.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DocumentChangeAction.cs new file mode 100644 index 0000000000000..903f096b846ee --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/DocumentChangeAction.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; + +namespace Microsoft.CodeAnalysis +{ + internal class DocumentChangeAction : CodeAction + { + private readonly string _title; + private readonly Func> _createChangedDocument; + + public DocumentChangeAction(string title, Func> createChangedDocument) + { + _title = title; + _createChangedDocument = createChangedDocument; + } + + public override string Title + { + get { return _title; } + } + + protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) + { + return _createChangedDocument(cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj new file mode 100644 index 0000000000000..18d0809a953fe --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj @@ -0,0 +1,81 @@ + + + + + + + + + 12.0 + Debug + AnyCPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8} + Library + true + System.Runtime.Analyzers + System.Runtime.Analyzers + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Profile7 + .NETPortable + true + + + + + + + + + {1ee8cad3-55f9-4d91-96b2-084641da9a6c} + CodeAnalysis + + + {5f8d2414-064a-4b3a-9b42-8e2a04246be5} + Workspaces + + + + + False + ..\..\..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll + + + + + + + + + + + + + + + + + + + True + True + SystemRuntimeAnalyzersResources.resx + + + + + + ResXFileCodeGenerator + SystemRuntimeAnalyzersResources.Designer.cs + + + + + + + + + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs new file mode 100644 index 0000000000000..b0f895fa2f7d9 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace System.Runtime.Analyzers { + using System; + using System.Reflection; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class SystemRuntimeAnalyzersResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SystemRuntimeAnalyzersResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Runtime.Analyzers.SystemRuntimeAnalyzersResources", typeof(SystemRuntimeAnalyzersResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Design. + /// + internal static string CategoryDesign { + get { + return ResourceManager.GetString("CategoryDesign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Globalization. + /// + internal static string CategoryGlobalization { + get { + return ResourceManager.GetString("CategoryGlobalization", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Interoperability. + /// + internal static string CategoryInteroperability { + get { + return ResourceManager.GetString("CategoryInteroperability", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Naming. + /// + internal static string CategoryNaming { + get { + return ResourceManager.GetString("CategoryNaming", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Performance. + /// + internal static string CategoryPerformance { + get { + return ResourceManager.GetString("CategoryPerformance", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reliability. + /// + internal static string CategoryReliability { + get { + return ResourceManager.GetString("CategoryReliability", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Usage. + /// + internal static string CategoryUsage { + get { + return ResourceManager.GetString("CategoryUsage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Implement IDisposable Interface. + /// + internal static string ImplementIDisposableInterface { + get { + return ResourceManager.GetString("ImplementIDisposableInterface", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type '{0}' owns disposable fields but is not disposable. + /// + internal static string TypeOwnsDisposableFieldButIsNotDisposable { + get { + return ResourceManager.GetString("TypeOwnsDisposableFieldButIsNotDisposable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Types that own disposable fields should be disposable. + /// + internal static string TypesThatOwnDisposableFieldsShouldBeDisposable { + get { + return ResourceManager.GetString("TypesThatOwnDisposableFieldsShouldBeDisposable", resourceCulture); + } + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx new file mode 100644 index 0000000000000..ceac435cd70bf --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Types that own disposable fields should be disposable + + + Design + + + Globalization + + + Interoperability + + + Naming + + + Performance + + + Reliability + + + Usage + + + Implement IDisposable Interface + + + Type '{0}' owns disposable fields but is not disposable + + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/WellKnownTypes.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/WellKnownTypes.cs new file mode 100644 index 0000000000000..5676c11a1af43 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/WellKnownTypes.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; + +namespace System.Runtime.Analyzers +{ + internal static class WellKnownTypes + { + public static INamedTypeSymbol FlagsAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.FlagsAttribute"); + } + + public static INamedTypeSymbol StringComparison(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.StringComparison"); + } + + public static INamedTypeSymbol CharSet(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.InteropServices.CharSet"); + } + + public static INamedTypeSymbol DllImportAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.InteropServices.DllImportAttribute"); + } + + public static INamedTypeSymbol MarshalAsAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.InteropServices.MarshalAsAttribute"); + } + + public static INamedTypeSymbol StringBuilder(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Text.StringBuilder"); + } + + public static INamedTypeSymbol UnmanagedType(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedType"); + } + + public static INamedTypeSymbol MarshalByRefObject(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.MarshalByRefObject"); + } + + public static INamedTypeSymbol ExecutionEngineException(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.ExecutionEngineException"); + } + + public static INamedTypeSymbol OutOfMemoryException(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.OutOfMemoryException"); + } + + public static INamedTypeSymbol StackOverflowException(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.StackOverflowException"); + } + + public static INamedTypeSymbol MemberInfo(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Reflection.MemberInfo"); + } + + public static INamedTypeSymbol ParameterInfo(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Reflection.ParameterInfo"); + } + + public static INamedTypeSymbol Thread(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Threading.Thread"); + } + + public static INamedTypeSymbol WebUIControl(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Web.UI.Control"); + } + + public static INamedTypeSymbol WinFormsUIControl(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Windows.Forms.Control"); + } + + public static INamedTypeSymbol IDisposable(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.IDisposable"); + } + + public static INamedTypeSymbol ISerializable(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.Serialization.ISerializable"); + } + + public static INamedTypeSymbol SerializationInfo(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.Serialization.SerializationInfo"); + } + + public static INamedTypeSymbol StreamingContext(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.Serialization.StreamingContext"); + } + + public static INamedTypeSymbol SerializableAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.SerializableAttribute"); + } + + public static INamedTypeSymbol NonSerializedAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.NonSerializedAttribute"); + } + + public static INamedTypeSymbol AttributeUsageAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.AttributeUsageAttribute"); + } + + public static INamedTypeSymbol AssemblyVersionAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Reflection.AssemblyVersionAttribute"); + } + + public static INamedTypeSymbol CLSCompliantAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.CLSCompliantAttribute"); + } + + public static INamedTypeSymbol ConditionalAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Diagnostics.ConditionalAttribute"); + } + + public static INamedTypeSymbol IComparable(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.IComparable"); + } + + public static INamedTypeSymbol ComSourceInterfaceAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.InteropServices.ComSourceInterfacesAttribute"); + } + + public static INamedTypeSymbol EventHandler(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.EventHandler"); + } + + public static INamedTypeSymbol GenericEventHandler(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.EventHandler`1"); + } + + public static INamedTypeSymbol EventArgs(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.EventArgs"); + } + + public static INamedTypeSymbol ComVisibleAttribute(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.Runtime.InteropServices.ComVisibleAttribute"); + } + + public static INamedTypeSymbol NotImplementedException(Compilation compilation) + { + return compilation.GetTypeByMetadataName("System.NotImplementedException"); + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/packages.config b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/packages.config new file mode 100644 index 0000000000000..7a5f834c82a3f --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj new file mode 100644 index 0000000000000..8fcaccde7c94f --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj @@ -0,0 +1,109 @@ + + + + + + + + + Debug + AnyCPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863} + Library + System.Runtime.Analyzers.UnitTests + System.Runtime.Analyzers.UnitTests + true + ..\..\..\..\ + true + + + + {dfa21ca1-7f96-47ee-940c-069858e81727} + CodeAnalysis.Desktop + + + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} + CodeAnalysis + + + {B501A547-C911-4A05-AC6E-274A50DFF30E} + CSharpCodeAnalysis + + + {73f3e2c5-d742-452e-b9e1-20732ddbc75d} + BasicCodeAnalysis.Desktop + + + {2523D0E6-DF32-4A3E-8AE0-A19BFFAE2EF6} + BasicCodeAnalysis + + + {76C6F005-C89D-4348-BB4A-391898DBEB52} + TestUtilities + + + {21B239D0-D144-430F-A394-C066D58EE267} + CSharpWorkspace + + + {57CA988D-F010-4BF2-9A2E-07D6DCD2FF2C} + BasicWorkspace + + + {5F8D2414-064A-4B3A-9B42-8E2A04246BE5} + Workspaces + + + {0A0621F2-D1DC-47FF-B643-C6646557505E} + DiagnosticsTestUtilities + + + {7FE6B002-89D8-4298-9B1B-0B5C247DD1FD} + CompilerTestResources + false + + + {baa0fee4-93c8-46f0-bb36-53a6053776c8} + SystemRuntimeAnalyzers + + + {a36451ec-1127-40ce-b841-47f393d24624} + CSharpSystemRuntimeAnalyzers + + + {d835c05e-9d83-40b2-9d25-19eb652f10d7} + BasicSystemRuntimeAnalyzers + + + + + + + + + + False + ..\..\..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/TypesThatOwnDisposableFieldsShouldBeDisposableTests.Fixer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/TypesThatOwnDisposableFieldsShouldBeDisposableTests.Fixer.cs new file mode 100644 index 0000000000000..eccb7c9f1df34 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/TypesThatOwnDisposableFieldsShouldBeDisposableTests.Fixer.cs @@ -0,0 +1,256 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.UnitTests; +using Roslyn.Test.Utilities; +using Xunit; + +namespace System.Runtime.Analyzers.UnitTests +{ + public partial class CA1001FixerTests : CodeFixTestBase + { + protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() + { + return new TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer(); + } + + protected override CodeFixProvider GetBasicCodeFixProvider() + { + return new TypesThatOwnDisposableFieldsShouldBeDisposableFixer(); + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer(); + } + + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return new TypesThatOwnDisposableFieldsShouldBeDisposableFixer(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1001CSharpCodeFixNoDispose() + { + VerifyCSharpFix(@" +using System; +using System.IO; + +// This class violates the rule. +public class NoDisposeClass +{ + FileStream newFile; + + public NoDisposeClass() + { + newFile = new FileStream("""", FileMode.Append); + } +} +", +@" +using System; +using System.IO; + +// This class violates the rule. +public class NoDisposeClass +: IDisposable +{ + FileStream newFile; + + public NoDisposeClass() + { + newFile = new FileStream("""", FileMode.Append); + } + + public void Dispose() + { + throw new NotImplementedException(); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1001BasicCodeFixNoDispose() + { + VerifyBasicFix(@" +Imports System +Imports System.IO + +' This class violates the rule. +Public Class NoDisposeMethod + + Dim newFile As FileStream + + Sub New() + newFile = New FileStream("""", FileMode.Append) + End Sub + +End Class +", +@" +Imports System +Imports System.IO + +' This class violates the rule. +Public Class NoDisposeMethod + Implements IDisposable + + Dim newFile As FileStream + + Sub New() + newFile = New FileStream("""", FileMode.Append) + End Sub + + Sub Dispose() Implements IDisposable.Dispose + Throw New NotImplementedException() + End Sub +End Class +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1001CSharpCodeFixHasDispose() + { + VerifyCSharpFix(@" +using System; +using System.IO; + +// This class violates the rule. +public class NoDisposeClass +{ + FileStream newFile; + + void Dispose() { +// Some content +} +} +", +@" +using System; +using System.IO; + +// This class violates the rule. +public class NoDisposeClass +: IDisposable +{ + FileStream newFile; + + public void Dispose() { +// Some content +} +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1001CSharpCodeFixHasWrongDispose() + { + VerifyCSharpFix(@" +using System; +using System.IO; + +// This class violates the rule. +public partial class NoDisposeClass +{ + FileStream newFile; + + void Dispose(int x) { +// Some content +} +} +", +@" +using System; +using System.IO; + +// This class violates the rule. +public partial class NoDisposeClass +: IDisposable +{ + FileStream newFile; + + void Dispose(int x) { +// Some content +} + + public void Dispose() + { + throw new NotImplementedException(); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1001BasicCodeFixHasDispose() + { + VerifyBasicFix(@" +Imports System +Imports System.IO + +' This class violates the rule. +Public Class NoDisposeMethod + + Dim newFile As FileStream + + Sub Dispose() + + End Sub +End Class +", +@" +Imports System +Imports System.IO + +' This class violates the rule. +Public Class NoDisposeMethod + Implements IDisposable + + Dim newFile As FileStream + + Sub Dispose() Implements IDisposable.Dispose + End Sub +End Class +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1001BasicCodeFixHasWrongDispose() + { + VerifyBasicFix(@" +Imports System +Imports System.IO + +' This class violates the rule. +Public Class NoDisposeMethod + + Dim newFile As FileStream + + Sub Dispose(x As Integer) + End Sub +End Class +", +@" +Imports System +Imports System.IO + +' This class violates the rule. +Public Class NoDisposeMethod + Implements IDisposable + + Dim newFile As FileStream + + Sub Dispose(x As Integer) + End Sub + + Sub Dispose() Implements IDisposable.Dispose + Throw New NotImplementedException() + End Sub +End Class +"); + } + } +} diff --git a/src/Diagnostics/FxCop/Test/Design/CA1001Tests.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/TypesThatOwnDisposableFieldsShouldBeDisposableTests.cs similarity index 96% rename from src/Diagnostics/FxCop/Test/Design/CA1001Tests.cs rename to src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/TypesThatOwnDisposableFieldsShouldBeDisposableTests.cs index 611f9f82aa54b..540ca3d31fc44 100644 --- a/src/Diagnostics/FxCop/Test/Design/CA1001Tests.cs +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/TypesThatOwnDisposableFieldsShouldBeDisposableTests.cs @@ -1,22 +1,22 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.FxCopAnalyzers.Design; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.UnitTests; using Xunit; -namespace Microsoft.CodeAnalysis.UnitTests +namespace System.Runtime.Analyzers.UnitTests { public partial class CA1001Tests : DiagnosticAnalyzerTestBase { protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() { - return new CA1001DiagnosticAnalyzer(); + return new TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer(); } protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() { - return new CA1001DiagnosticAnalyzer(); + return new TypesThatOwnDisposableFieldsShouldBeDisposableAnalyzer(); } [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/packages.config b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/packages.config new file mode 100644 index 0000000000000..a972616f20fdd --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj new file mode 100644 index 0000000000000..ede2e04d6cee0 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj @@ -0,0 +1,79 @@ + + + + + + + + + 12.0 + Debug + AnyCPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7} + Library + true + Microsoft.CodeAnalysis.VisualBasic.Analyzers + {14182A97-F7F0-4C62-8B27-98AA8AE2109A};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} + Profile7 + .NETPortable + true + + + + + + + + + {1ee8cad3-55f9-4d91-96b2-084641da9a6c} + CodeAnalysis + + + {2523d0e6-df32-4a3e-8ae0-a19bffae2ef6} + BasicCodeAnalysis + + + {5f8d2414-064a-4b3a-9b42-8e2a04246be5} + Workspaces + + + {d8762a0a-3832-47be-bcf6-8b1060be6b28} + CodeAnalysisDiagnosticAnalyzers + + + + + False + ..\..\..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll + + + False + ..\..\..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll + + + + + + + + + + + + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/packages.config b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/packages.config new file mode 100644 index 0000000000000..7a5f834c82a3f --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Diagnostics/FxCop/Test/Design/CodeFixes/CA1001FixerTests.cs b/src/Diagnostics/FxCop/Test/Design/CodeFixes/CA1001FixerTests.cs deleted file mode 100644 index 8cc96629caea9..0000000000000 --- a/src/Diagnostics/FxCop/Test/Design/CodeFixes/CA1001FixerTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.FxCopAnalyzers.Design; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.FxCopAnalyzers.Design; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.VisualBasic.FxCopAnalyzers.Design; -using Xunit; - -namespace Microsoft.CodeAnalysis.UnitTests -{ - public partial class CA1001FixerTests : CodeFixTestBase - { - protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return new CA1001DiagnosticAnalyzer(); - } - - protected override CodeFixProvider GetBasicCodeFixProvider() - { - return new CA1001BasicCodeFixProvider(); - } - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new CA1001DiagnosticAnalyzer(); - } - - protected override CodeFixProvider GetCSharpCodeFixProvider() - { - return new CA1001CSharpCodeFixProvider(); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] - public void CA1001CSharpCodeFixNoEqualsOperator() - { - VerifyCSharpFix(@" -using System; -using System.IO; - -// This class violates the rule. -public class NoDisposeClass -{ - FileStream newFile; - - public NoDisposeClass() - { - newFile = new FileStream("""", FileMode.Append); - } -} -", -@" -using System; -using System.IO; - -// This class violates the rule. -public class NoDisposeClass : IDisposable -{ - FileStream newFile; - - public NoDisposeClass() - { - newFile = new FileStream("""", FileMode.Append); - } - - public void Dispose() - { - throw new NotImplementedException(); - } -} -"); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] - public void CA1001BasicCodeFixNoEqualsOperator() - { - VerifyBasicFix(@" -Imports System -Imports System.IO - -' This class violates the rule. -Public Class NoDisposeMethod - - Dim newFile As FileStream - - Sub New() - newFile = New FileStream("""", FileMode.Append) - End Sub - -End Class -", -@" -Imports System -Imports System.IO - -' This class violates the rule. -Public Class NoDisposeMethod - Implements IDisposable - - Dim newFile As FileStream - - Sub New() - newFile = New FileStream("""", FileMode.Append) - End Sub - - Public Sub Dispose() Implements IDisposable.Dispose - Throw New NotImplementedException() - End Sub -End Class -"); - } - } -} diff --git a/src/Diagnostics/FxCop/Test/FxCopRulesDiagnosticAnalyzersTest.csproj b/src/Diagnostics/FxCop/Test/FxCopRulesDiagnosticAnalyzersTest.csproj index 60d2af1789593..5e4f45f6d0694 100644 --- a/src/Diagnostics/FxCop/Test/FxCopRulesDiagnosticAnalyzersTest.csproj +++ b/src/Diagnostics/FxCop/Test/FxCopRulesDiagnosticAnalyzersTest.csproj @@ -80,7 +80,6 @@ - @@ -93,7 +92,6 @@ - @@ -150,4 +148,4 @@ - + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/VisualBasic/BasicFxCopRulesDiagnosticAnalyzers.vbproj b/src/Diagnostics/FxCop/VisualBasic/BasicFxCopRulesDiagnosticAnalyzers.vbproj index 13959b07250cb..90c94cb6f9b52 100644 --- a/src/Diagnostics/FxCop/VisualBasic/BasicFxCopRulesDiagnosticAnalyzers.vbproj +++ b/src/Diagnostics/FxCop/VisualBasic/BasicFxCopRulesDiagnosticAnalyzers.vbproj @@ -99,7 +99,6 @@ - @@ -132,4 +131,4 @@ - + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/VisualBasic/Design/CodeFixes/CA1001BasicCodeFixProvider.vb b/src/Diagnostics/FxCop/VisualBasic/Design/CodeFixes/CA1001BasicCodeFixProvider.vb deleted file mode 100644 index 3a07a99fea72d..0000000000000 --- a/src/Diagnostics/FxCop/VisualBasic/Design/CodeFixes/CA1001BasicCodeFixProvider.vb +++ /dev/null @@ -1,82 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports System.Composition -Imports System.Threading -Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.CodeFixes -Imports Microsoft.CodeAnalysis.Formatting -Imports Microsoft.CodeAnalysis.FxCopAnalyzers.Design -Imports Microsoft.CodeAnalysis.Simplification -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax - -Namespace Microsoft.CodeAnalysis.VisualBasic.FxCopAnalyzers.Design - ' - ' CA1001: Types that own disposable fields should be disposable - ' - - Public Class CA1001BasicCodeFixProvider - Inherits CA1001CodeFixProviderBase - - Friend Overrides Function GetUpdatedDocumentAsync(document As Document, model As SemanticModel, root As SyntaxNode, nodeToFix As SyntaxNode, diagnostic As Diagnostic, cancellationToken As CancellationToken) As Task(Of Document) - ' We are going to implement IDisposable interface - ' - ' Public Sub Dispose() Implements IDisposable.Dispose - ' Throw New NotImplementedException() - ' End Sub - - Dim syntaxNode = TryCast(nodeToFix, ClassStatementSyntax) - If syntaxNode Is Nothing Then - Return Task.FromResult(document) - End If - - Dim statement = CreateThrowNotImplementedStatement(model) - If statement Is Nothing Then - Return Task.FromResult(document) - End If - - Dim member = CreateSimpleSubBlockDeclaration(CA1001DiagnosticAnalyzer.Dispose, statement) - Dim parent = DirectCast(syntaxNode.Parent, ClassBlockSyntax) - Dim implementsStatement = SyntaxFactory.ImplementsStatement( - SyntaxFactory.ParseTypeName(CA1001DiagnosticAnalyzer.IDisposable) _ - .WithAdditionalAnnotations(Simplifier.Annotation)) _ - .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed) - - Dim newNode = parent.AddMembers(New MethodBlockSyntax() {member}).AddImplements(implementsStatement).WithAdditionalAnnotations(Formatter.Annotation) - Return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(parent, newNode))) - End Function - - Protected Function CreateThrowNotImplementedStatement(model As SemanticModel) As StatementSyntax - Dim exceptionType = model.Compilation.GetTypeByMetadataName(NotImplementedExceptionName) - If exceptionType Is Nothing Then - ' If we can't find the exception, we can't generate anything. - Return Nothing - End If - - Return SyntaxFactory.ThrowStatement( - SyntaxFactory.ObjectCreationExpression( - Nothing, - SyntaxFactory.ParseTypeName(exceptionType.Name), - SyntaxFactory.ArgumentList(), - Nothing)) - End Function - - Protected Function CreateSimpleSubBlockDeclaration(name As String, statement As StatementSyntax) As MethodBlockSyntax - Return SyntaxFactory.SubBlock( - SyntaxFactory.SubStatement( - Nothing, - SyntaxFactory.TokenList(New SyntaxToken() {SyntaxFactory.Token(SyntaxKind.PublicKeyword)}), - SyntaxFactory.Token(SyntaxKind.SubKeyword), - SyntaxFactory.Identifier(name), - Nothing, - SyntaxFactory.ParameterList(), - Nothing, - Nothing, - SyntaxFactory.ImplementsClause( - SyntaxFactory.QualifiedName( - SyntaxFactory.ParseName(CA1001DiagnosticAnalyzer.IDisposable), - SyntaxFactory.IdentifierName(CA1001DiagnosticAnalyzer.Dispose)) _ - .WithAdditionalAnnotations(Simplifier.Annotation))), - SyntaxFactory.SingletonList(statement)) - End Function - End Class -End Namespace diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 031328a063af5..8c32d4ce8aa82 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -224,6 +224,7 @@ + @@ -614,10 +615,7 @@ PreserveNewest - - - - + diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/FixAllProvider/BatchFixerTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/FixAllProvider/BatchFixerTests.cs new file mode 100644 index 0000000000000..0ad40aa333f6e --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Diagnostics/FixAllProvider/BatchFixerTests.cs @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.SimplifyTypeNames +{ + public partial class BatchFixerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) + { + return Tuple.Create(new QualifyWithThisAnalyzer(), new QualifyWithThisFixer()); + } + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + private class QualifyWithThisAnalyzer : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor Descriptor = new TriggerDiagnosticDescriptor("QualifyWithThis"); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(Descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.IdentifierName); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var node = context.Node as SimpleNameSyntax; + if (node != null) + { + var symbol = context.SemanticModel.GetSymbolInfo(node).Symbol; + if (symbol != null && symbol.Kind == SymbolKind.Field) + { + var diagnostic = Diagnostic.Create(Descriptor, node.GetLocation()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + private class QualifyWithThisFixer : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds + { + get + { + return ImmutableArray.Create(QualifyWithThisAnalyzer.Descriptor.Id); + } + } + + public async override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(context.Span, getInnermostNodeForTie: true) as SimpleNameSyntax; + if (node != null) + { + var leadingTrivia = node.GetLeadingTrivia(); + var newNode = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + node.WithoutLeadingTrivia()) + .WithLeadingTrivia(leadingTrivia); + + var newRoot = root.ReplaceNode(node, newNode); + var newDocument = context.Document.WithSyntaxRoot(newRoot); + + // Disable RS0005 as this is test code and we don't need telemtry for created code action. +#pragma warning disable RS0005 // Do not use generic CodeAction.Create to create CodeAction + var fix = CodeAction.Create("QualifyWithThisFix", _ => Task.FromResult(newDocument)); +#pragma warning restore RS0005 // Do not use generic CodeAction.Create to create CodeAction + + context.RegisterCodeFix(fix, context.Diagnostics); + } + } + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + } + + #region "Fix all occurrences tests" + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/320")] + [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public void TestFixAllInDocument_QualifyWithThis() + { + var input = @" + + + +class C +{ + int Sign; + void F() + { + string x = @""namespace Namespace + { + class Type + { + void Foo() + { + int x = 1 "" + {|FixAllInDocument:Sign|} + @"" "" + Sign + @""3; + } + } + } +""; + } +} + + +"; + + var expected = @" + + + +class C +{ + int Sign; + void F() + { + string x = @""namespace Namespace + { + class Type + { + void Foo() + { + int x = 1 "" + this.Sign + @"" "" + this.Sign + @""3; + } + } + } +""; + } +} + + +"; + + Test(input, expected, isLine: false, compareTokens: false); + } + + #endregion + } +} diff --git a/src/EditorFeatures/Core/Extensibility/Navigation/NavigableItemFactory.DeclaredSymbolNavigableItem.cs b/src/EditorFeatures/Core/Extensibility/Navigation/NavigableItemFactory.DeclaredSymbolNavigableItem.cs index d03e168d4ce7b..d2654499db228 100644 --- a/src/EditorFeatures/Core/Extensibility/Navigation/NavigableItemFactory.DeclaredSymbolNavigableItem.cs +++ b/src/EditorFeatures/Core/Extensibility/Navigation/NavigableItemFactory.DeclaredSymbolNavigableItem.cs @@ -2,10 +2,12 @@ using System; using System.Threading; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Navigation { @@ -33,24 +35,31 @@ public DeclaredSymbolNavigableItem(Document document, DeclaredSymbolInfo declare _lazySymbol = new Lazy(() => declaredSymbolInfo.GetSymbolAsync(document, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()); _lazyDisplayName = new Lazy(() => { - if (Symbol == null) + try { - return null; - } + if (Symbol == null) + { + return null; + } - var symbolDisplayService = Document.GetLanguageService(); - switch (Symbol.Kind) - { - case SymbolKind.NamedType: - return symbolDisplayService.ToDisplayString(Symbol, s_shortFormatWithModifiers); + var symbolDisplayService = Document.GetLanguageService(); + switch (Symbol.Kind) + { + case SymbolKind.NamedType: + return symbolDisplayService.ToDisplayString(Symbol, s_shortFormatWithModifiers); - case SymbolKind.Method: - return Symbol.IsStaticConstructor() - ? symbolDisplayService.ToDisplayString(Symbol, s_shortFormatWithModifiers) - : symbolDisplayService.ToDisplayString(Symbol, s_shortFormat); + case SymbolKind.Method: + return Symbol.IsStaticConstructor() + ? symbolDisplayService.ToDisplayString(Symbol, s_shortFormatWithModifiers) + : symbolDisplayService.ToDisplayString(Symbol, s_shortFormat); - default: - return symbolDisplayService.ToDisplayString(Symbol, s_shortFormat); + default: + return symbolDisplayService.ToDisplayString(Symbol, s_shortFormat); + } + } + catch (Exception e) when (FatalError.Report(e)) + { + throw ExceptionUtilities.Unreachable; } }); } diff --git a/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb b/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb index 346cd2bf81b4d..a9f4090df4e04 100644 --- a/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb +++ b/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb @@ -541,6 +541,16 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.NavigateTo End Using End Sub + + + Public Sub FindClassInGlobalNamespace() + Using worker = SetupWorkspace("Namespace Global", "Public Class C(Of T)", "End Class", "End Namespace") + SetupVerifableGlyph(StandardGlyphGroup.GlyphGroupClass, StandardGlyphItem.GlyphItemPublic) + Dim item = _aggregator.GetItems("C").Single + VerifyNavigateToResultItem(item, "C", MatchKind.Exact, NavigateToItemKind.Class, displayName:="C(Of T)") + End Using + End Sub + Public Sub StartStopSanity() ' Verify that mutliple calls to start/stop don't blow up diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj index a9441ba967cfd..947e1e38ce24c 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj @@ -79,6 +79,7 @@ + diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs index 11fa7caa89349..63ffdb55f5eb3 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using Microsoft.CodeAnalysis.ExpressionEvaluator; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator { [DkmReportNonFatalWatsonException(ExcludeExceptionType = typeof(NotImplementedException)), DkmContinueCorruptingException] - internal sealed class CSharpFrameDecoder : FrameDecoder + internal sealed class CSharpFrameDecoder : FrameDecoder { public CSharpFrameDecoder() : base(CSharpInstructionDecoder.Instance) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs index 0c155cc3954c9..772ecc5898a15 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Diagnostics; +using System.Collections.Immutable; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; @@ -9,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator { - internal sealed class CSharpInstructionDecoder : InstructionDecoder + internal sealed class CSharpInstructionDecoder : InstructionDecoder { // This string was not localized in the old EE. We'll keep it that way // so as not to break consumers who may have been parsing frame names... @@ -28,7 +30,7 @@ private CSharpInstructionDecoder() AddMemberOptions(SymbolDisplayMemberOptions.IncludeParameters). WithParameterOptions(SymbolDisplayParameterOptions.IncludeType); - internal override void AppendFullName(StringBuilder builder, PEMethodSymbol method) + internal override void AppendFullName(StringBuilder builder, MethodSymbol method) { var displayFormat = ((method.MethodKind == MethodKind.PropertyGet) || (method.MethodKind == MethodKind.PropertySet)) ? @@ -86,7 +88,28 @@ internal override void AppendFullName(StringBuilder builder, PEMethodSymbol meth } } - internal override PEMethodSymbol GetMethod(DkmClrInstructionAddress instructionAddress) + internal override MethodSymbol ConstructMethod(MethodSymbol method, ImmutableArray typeParameters, ImmutableArray typeArguments) + { + var methodArity = method.Arity; + var methodArgumentStartIndex = typeParameters.Length - methodArity; + var typeMap = new TypeMap( + ImmutableArray.Create(typeParameters, 0, methodArgumentStartIndex), + ImmutableArray.Create(typeArguments, 0, methodArgumentStartIndex)); + var substitutedType = typeMap.SubstituteNamedType(method.ContainingType); + method = method.AsMember(substitutedType); + if (methodArity > 0) + { + method = method.Construct(ImmutableArray.Create(typeArguments, methodArgumentStartIndex, methodArity)); + } + return method; + } + + internal override ImmutableArray GetAllTypeParameters(MethodSymbol method) + { + return method.GetAllTypeParameters(); + } + + internal override CSharpCompilation GetCompilation(DkmClrInstructionAddress instructionAddress) { var moduleInstance = instructionAddress.ModuleInstance; var appDomain = moduleInstance.AppDomain; @@ -105,7 +128,18 @@ internal override PEMethodSymbol GetMethod(DkmClrInstructionAddress instructionA compilation = dataItem.Compilation; } - return compilation.GetSourceMethod(moduleInstance.Mvid, instructionAddress.MethodId.Token); + return compilation; + } + + internal override MethodSymbol GetMethod(CSharpCompilation compilation, DkmClrInstructionAddress instructionAddress) + { + return compilation.GetSourceMethod(instructionAddress.ModuleInstance.Mvid, instructionAddress.MethodId.Token); + } + + internal override TypeNameDecoder GetTypeNameDecoder(CSharpCompilation compilation, MethodSymbol method) + { + Debug.Assert(method is PEMethodSymbol); + return new EETypeNameDecoder(compilation, (PEModuleSymbol)method.ContainingModule); } } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs index f068b0364d1cf..5787513d95ebc 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs @@ -2,16 +2,17 @@ using System; using Microsoft.CodeAnalysis.ExpressionEvaluator; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator { [DkmReportNonFatalWatsonException(ExcludeExceptionType = typeof(NotImplementedException)), DkmContinueCorruptingException] - internal sealed class CSharpLanguageInstructionDecoder : LanguageInstructionDecoder + internal sealed class CSharpLanguageInstructionDecoder : LanguageInstructionDecoder { public CSharpLanguageInstructionDecoder() : base(CSharpInstructionDecoder.Instance) { } } -} +} \ No newline at end of file diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs index 4fd8220a4b1c4..a1854491f8e05 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs @@ -245,14 +245,6 @@ internal CommonPEModuleBuilder CompileAssignment( return module; } - private static ImmutableArray GetAllTypeParameters(MethodSymbol method) - { - var builder = ArrayBuilder.GetInstance(); - method.ContainingType.GetAllTypeParameters(builder); - builder.AddRange(method.TypeParameters); - return builder.ToImmutableAndFree(); - } - private static string GetNextMethodName(ArrayBuilder builder) { return string.Format("<>m{0}", builder.Count); @@ -270,7 +262,7 @@ internal CommonPEModuleBuilder CompileGetLocals( DiagnosticBag diagnostics) { var objectType = this.Compilation.GetSpecialType(SpecialType.System_Object); - var allTypeParameters = GetAllTypeParameters(_currentFrame); + var allTypeParameters = _currentFrame.GetAllTypeParameters(); var additionalTypes = ArrayBuilder.GetInstance(); EENamedTypeSymbol typeVariablesType = null; @@ -315,10 +307,10 @@ internal CommonPEModuleBuilder CompileGetLocals( } // Hoisted method parameters (represented as locals in the EE). - int ordinal = 0; if (!_hoistedParameterNames.IsEmpty) { - foreach (var local in _localsForBinding) + int localIndex = 0; + foreach(var local in _localsForBinding) { // Since we are showing hoisted method parameters first, the parameters may appear out of order // in the Locals window if only some of the parameters are hoisted. This is consistent with the @@ -326,36 +318,39 @@ internal CommonPEModuleBuilder CompileGetLocals( var localName = local.Name; if (_hoistedParameterNames.Contains(local.Name)) { - AppendLocalAndMethod(localBuilder, methodBuilder, localName, this.GetLocalMethod, container, ordinal, GetLocalResultFlags(local)); + AppendLocalAndMethod(localBuilder, methodBuilder, localName, this.GetLocalMethod, container, localIndex, GetLocalResultFlags(local)); } - ordinal++; + + localIndex++; } } // Method parameters (except those that have been hoisted). - ordinal = m.IsStatic ? 0 : 1; + int parameterIndex = m.IsStatic ? 0 : 1; foreach (var parameter in m.Parameters) { var parameterName = parameter.Name; if (!_hoistedParameterNames.Contains(parameterName)) { - AppendLocalAndMethod(localBuilder, methodBuilder, parameterName, this.GetParameterMethod, container, ordinal, DkmClrCompilationResultFlags.None); + AppendLocalAndMethod(localBuilder, methodBuilder, parameterName, this.GetParameterMethod, container, parameterIndex, DkmClrCompilationResultFlags.None); } - ordinal++; + + parameterIndex++; } if (!argumentsOnly) { // Locals. - ordinal = 0; + int localIndex = 0; foreach (var local in _localsForBinding) { var localName = local.Name; if (!_hoistedParameterNames.Contains(localName)) { - AppendLocalAndMethod(localBuilder, methodBuilder, localName, this.GetLocalMethod, container, ordinal, GetLocalResultFlags(local)); + AppendLocalAndMethod(localBuilder, methodBuilder, localName, this.GetLocalMethod, container, localIndex, GetLocalResultFlags(local)); } - ordinal++; + + localIndex++; } // "Type variables". @@ -401,7 +396,7 @@ private static void AppendLocalAndMethod( string name, Func getMethod, EENamedTypeSymbol container, - int ordinal, + int localOrParameterIndex, DkmClrCompilationResultFlags resultFlags) { // Note: The native EE doesn't do this, but if we don't escape keyword identifiers, @@ -409,7 +404,7 @@ private static void AppendLocalAndMethod( // which it can't do correctly without semantic information. name = SyntaxHelpers.EscapeKeywordIdentifiers(name); var methodName = GetNextMethodName(methodBuilder); - var method = getMethod(container, methodName, name, ordinal); + var method = getMethod(container, methodName, name, localOrParameterIndex); localBuilder.Add(new LocalAndMethod(name, methodName, resultFlags)); methodBuilder.Add(method); } @@ -446,23 +441,23 @@ internal EEMethodSymbol CreateMethod( generateMethodBody); } - private EEMethodSymbol GetLocalMethod(EENamedTypeSymbol container, string methodName, string localName, int index) + private EEMethodSymbol GetLocalMethod(EENamedTypeSymbol container, string methodName, string localName, int localIndex) { var syntax = SyntaxFactory.IdentifierName(localName); return this.CreateMethod(container, methodName, syntax, (method, diagnostics) => { - var local = method.LocalsForBinding[index]; + var local = method.LocalsForBinding[localIndex]; var expression = new BoundLocal(syntax, local, constantValueOpt: local.GetConstantValue(null, null, diagnostics), type: local.Type) { WasCompilerGenerated = true }; return new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; }); } - private EEMethodSymbol GetParameterMethod(EENamedTypeSymbol container, string methodName, string parameterName, int ordinal) + private EEMethodSymbol GetParameterMethod(EENamedTypeSymbol container, string methodName, string parameterName, int parameterIndex) { var syntax = SyntaxFactory.IdentifierName(parameterName); return this.CreateMethod(container, methodName, syntax, (method, diagnostics) => { - var parameter = method.Parameters[ordinal]; + var parameter = method.Parameters[parameterIndex]; var expression = new BoundParameter(syntax, parameter) { WasCompilerGenerated = true }; return new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; }); diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymbolExtensions.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymbolExtensions.cs new file mode 100644 index 0000000000000..d498e56290b11 --- /dev/null +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymbolExtensions.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator +{ + internal static class SymbolExtensions + { + internal static ImmutableArray GetAllTypeParameters(this MethodSymbol method) + { + var builder = ArrayBuilder.GetInstance(); + method.ContainingType.GetAllTypeParameters(builder); + builder.AddRange(method.TypeParameters); + return builder.ToImmutableAndFree(); + } + } +} diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index de325e47410c0..64259534d85ab 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -19,6 +19,15 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator /// internal sealed class EEMethodSymbol : MethodSymbol { + // We only create a single EE method (per EE type) that represents an arbitrary expression, + // whose lowering may produce synthesized members (lambdas, dynamic sites, etc). + // We may thus assume that the method ordinal is always 0. + // + // Consider making the implementation more flexible in order to avoid this assumption. + // In future we might need to compile multiple expression and then we'll need to assign + // a unique method ordinal to each of them to avoid duplicate synthesized member names. + private const int _methodOrdinal = 0; + internal readonly TypeMap TypeMap; internal readonly MethodSymbol SubstitutedSourceMethod; internal readonly ImmutableArray Locals; @@ -31,6 +40,7 @@ internal sealed class EEMethodSymbol : MethodSymbol private readonly ImmutableArray _parameters; private readonly ParameterSymbol _thisParameter; private readonly ImmutableDictionary _displayClassVariables; + /// /// Invoked at most once to generate the method body. /// (If the compilation has no errors, it will be invoked @@ -472,7 +482,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, body = LocalRewriter.Rewrite( compilation: this.DeclaringCompilation, method: this, - methodOrdinal: 0, + methodOrdinal: _methodOrdinal, containingType: _container, statement: body, compilationState: compilationState, @@ -544,7 +554,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, thisType: this.SubstitutedSourceMethod.ContainingType, thisParameter: _thisParameter, method: this, - methodOrdinal: 0, + methodOrdinal: _methodOrdinal, closureDebugInfoBuilder: closureDebugInfoBuilder, lambdaDebugInfoBuilder: lambdaDebugInfoBuilder, slotAllocatorOpt: null, diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs index 7e33eba502981..26f3a00c48a8b 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Diagnostics; +using System.Linq; using System.Reflection.Metadata.Ecma335; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.ExpressionEvaluator; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; @@ -14,8 +17,90 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator { public class InstructionDecoderTests : ExpressionCompilerTestBase { + [Fact] + void GetNameGenerics() + { + var source = @" +using System; +class Class1 +{ + void M1(Action a) + { + } + void M2(Action a) + { + } + void M3(Action a) + { + } +}"; + + Assert.Equal( + "Class1.M1(System.Action a)", + GetName(source, "Class1.M1", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)); + + Assert.Equal( + "Class1.M2(System.Action a)", + GetName(source, "Class1.M2", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)); + + Assert.Equal( + "Class1.M3(System.Action a)", + GetName(source, "Class1.M3", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)); + + Assert.Equal( + "Class1.M1(System.Action a)", + GetName(source, "Class1.M1", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(string), typeof(decimal) })); + + Assert.Equal( + "Class1.M2(System.Action a)", + GetName(source, "Class1.M2", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(string), typeof(decimal) })); + + Assert.Equal( + "Class1.M3(System.Action a)", + GetName(source, "Class1.M3", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(string), typeof(decimal) })); + } + + [Fact] + void GetNameNullTypeArguments() + { + var source = @" +using System; +class Class1 +{ + void M(Action a) + { + } +}"; + + Assert.Equal( + "Class1.M(System.Action a)", + GetName(source, "Class1.M", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new Type[] { null, null })); + + Assert.Equal( + "Class1.M(System.Action a)", + GetName(source, "Class1.M", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new[] { typeof(string), null })); + + Assert.Equal( + "Class1.M(System.Action a)", + GetName(source, "Class1.M", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new[] { null, typeof(decimal) })); + } + + [Fact] + void GetNameGenericArgumentTypeNotInReferences() + { + var source = @" +class Class1 +{ +}"; + + var serializedTypeArgumentName = "Class1, " + nameof(InstructionDecoderTests) + ", Culture=neutral, PublicKeyToken=null"; + Assert.Equal( + "System.Collections.Generic.Comparer.Create(System.Comparison comparison)", + GetName(source, "System.Collections.Generic.Comparer.Create", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new[] { serializedTypeArgumentName })); + } + [Fact, WorkItem(1107977)] - public void GetNameGenericAsync() + void GetNameGenericAsync() { var source = @" using System.Threading.Tasks; @@ -29,12 +114,12 @@ static async Task M(T x) }"; Assert.Equal( - "C.M(T x)", - GetName(source, "C.d__0.MoveNext", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)); + "C.M(System.Exception x)", + GetName(source, "C.d__0.MoveNext", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(Exception) })); } [Fact] - public void GetNameLambda() + void GetNameLambda() { var source = @" using System; @@ -52,7 +137,7 @@ void M() } [Fact] - public void GetNameGenericLambda() + void GetNameGenericLambda() { var source = @" using System; @@ -65,12 +150,12 @@ void M() where U : T }"; Assert.Equal( - "C.M.AnonymousMethod__0_0(U u)", - GetName(source, "C.<>c__0.b__0_0", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)); + "C.M.AnonymousMethod__0_0(System.ArgumentException u)", + GetName(source, "C.<>c__0.b__0_0", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(Exception), typeof(ArgumentException) })); } [Fact] - public void GetNameProperties() + void GetNameProperties() { var source = @" class C @@ -101,7 +186,7 @@ int this[object x] } [Fact] - public void GetNameExplicitInterfaceImplementation() + void GetNameExplicitInterfaceImplementation() { var source = @" using System; @@ -116,7 +201,7 @@ void IDisposable.Dispose() { } } [Fact] - public void GetNameExtensionMethod() + void GetNameExtensionMethod() { var source = @" static class Extensions @@ -130,7 +215,7 @@ static void M(this string @this) { } } [Fact] - public void GetNameArgumentFlagsNone() + void GetNameArgumentFlagsNone() { var source = @" static class C @@ -148,41 +233,154 @@ static void M2(int x, int y) { } GetName(source, "C.M2", DkmVariableInfoFlags.None)); } - private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, params string[] argumentValues) + [Fact] + void GetReturnTypeNamePrimitive() + { + var source = @" +static class C +{ + static uint M1() { return 42; } +}"; + + Assert.Equal("uint", GetReturnTypeName(source, "C.M1")); + } + + [Fact] + void GetReturnTypeNameNested() + { + var source = @" +static class C +{ + static N.D.E M1() { return default(N.D.E); } +} +namespace N +{ + class D + { + internal struct E + { + } + } +}"; + + Assert.Equal("N.D.E", GetReturnTypeName(source, "C.M1")); + } + + [Fact] + void GetReturnTypeNameGenericOfPrimitive() + { + var source = @" +using System; +class C +{ + Action M1() { return null; } +}"; + + Assert.Equal("System.Action", GetReturnTypeName(source, "C.M1")); + } + + [Fact] + void GetReturnTypeNameGenericOfNested() + { + var source = @" +using System; +class C +{ + Action M1() { return null; } + class D + { + } +}"; + + Assert.Equal("System.Action", GetReturnTypeName(source, "C.M1")); + } + + [Fact] + void GetReturnTypeNameGenericOfGeneric() + { + var source = @" +using System; +class C +{ + Action> M1() { return null; } +}"; + + Assert.Equal("System.Action>", GetReturnTypeName(source, "C.M1", new[] { typeof(object) })); + } + + private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, Type[] typeArguments = null, string[] argumentValues = null) + { + var serializedTypeArgumentNames = typeArguments?.Select(t => t?.AssemblyQualifiedName).ToArray(); + return GetName(source, methodName, argumentFlags, serializedTypeArgumentNames, argumentValues); + } + + private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, string[] typeArguments, string[] argumentValues = null) { Debug.Assert((argumentFlags & (DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)) == argumentFlags, "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags); - var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugDll); - var runtime = CreateRuntimeInstance(compilation); - var moduleInstances = runtime.Modules; - var blocks = moduleInstances.SelectAsArray(m => m.MetadataBlock); - compilation = blocks.ToCompilation(); - var frame = (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName); + var instructionDecoder = CSharpInstructionDecoder.Instance; + var method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder); - // Once we have the method token, we want to look up the method (again) - // using the same helper as the product code. This helper will also map - // async/iterator "MoveNext" methods to the original source method. - var method = compilation.GetSourceMethod( - ((PEModuleSymbol)frame.ContainingModule).Module.GetModuleVersionIdOrThrow(), - MetadataTokens.GetToken(frame.Handle)); var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); ArrayBuilder builder = null; - if (argumentValues.Length > 0) + if (argumentValues != null) { + Assert.InRange(argumentValues.Length, 1, int.MaxValue); builder = ArrayBuilder.GetInstance(); builder.AddRange(argumentValues); } - var frameDecoder = CSharpInstructionDecoder.Instance; - var frameName = frameDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder); + var name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder); if (builder != null) { builder.Free(); } - return frameName; + return name; + } + + private string GetReturnTypeName(string source, string methodName, Type[] typeArguments = null) + { + var instructionDecoder = CSharpInstructionDecoder.Instance; + var serializedTypeArgumentNames = typeArguments?.Select(t => (t != null) ? t.AssemblyQualifiedName : null).ToArray(); + var method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder); + + return instructionDecoder.GetReturnTypeName(method); + } + + private MethodSymbol GetConstructedMethod(string source, string methodName, string[] serializedTypeArgumentNames, CSharpInstructionDecoder instructionDecoder) + { + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugDll, assemblyName: nameof(InstructionDecoderTests)); + var runtime = CreateRuntimeInstance(compilation); + var moduleInstances = runtime.Modules; + var blocks = moduleInstances.SelectAsArray(m => m.MetadataBlock); + compilation = blocks.ToCompilation(); + var frame = (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName); + + // Once we have the method token, we want to look up the method (again) + // using the same helper as the product code. This helper will also map + // async/iterator "MoveNext" methods to the original source method. + MethodSymbol method = compilation.GetSourceMethod( + ((PEModuleSymbol)frame.ContainingModule).Module.GetModuleVersionIdOrThrow(), + MetadataTokens.GetToken(frame.Handle)); + if (serializedTypeArgumentNames != null) + { + Assert.NotEmpty(serializedTypeArgumentNames); + var typeParameters = instructionDecoder.GetAllTypeParameters(method); + Assert.NotEmpty(typeParameters); + var typeNameDecoder = new EETypeNameDecoder(compilation, (PEModuleSymbol)method.ContainingModule); + // Use the same helper method as the FrameDecoder to get the TypeSymbols for the + // generic type arguments (rather than using EETypeNameDecoder directly). + var typeArguments = instructionDecoder.GetTypeSymbols(compilation, method, serializedTypeArgumentNames); + if (!typeArguments.IsEmpty) + { + method = instructionDecoder.ConstructMethod(method, typeParameters, typeArguments); + } + } + + return method; } } -} +} \ No newline at end of file diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj index f8f350da30a71..4a5b4f895fd94 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.csproj @@ -18,9 +18,6 @@ - - Shared\MetadataReaderPdbExtensions.cs - Shared\SymUnmanagedReaderExtensions.cs diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs index 30601e88a1587..77d27323a45b7 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs @@ -4,11 +4,12 @@ using System.Diagnostics; using System.Reflection; using Microsoft.VisualStudio.Debugger; +using Roslyn.Utilities; #if !EXPRESSIONCOMPILER using Microsoft.CodeAnalysis.ErrorReporting; - #endif + namespace Microsoft.CodeAnalysis.ExpressionEvaluator { internal static class ExpressionEvaluatorFatalError @@ -80,5 +81,20 @@ internal static bool CrashIfFailFastEnabled(Exception exception) return FatalError.Report(exception); } + + internal delegate bool NonFatalExceptionHandler(Exception exception, string implementationName); + + internal static bool ReportNonFatalException(Exception exception, NonFatalExceptionHandler handler) + { + if (CrashIfFailFastEnabled(exception)) + { + throw ExceptionUtilities.Unreachable; + } + + // Ignore the return value, because we always want to continue after reporting the Exception. + handler(exception, nameof(ExpressionEvaluatorFatalError)); + + return true; + } } -} +} \ No newline at end of file diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs index 0ab210df78e5f..f3e1b24a68b84 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; using System.Diagnostics; using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.CallStack; @@ -19,86 +20,35 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator /// always used C# syntax (but with language-specific "special names"). Since these names are exposed through public /// APIs, we will remain consistent with the old behavior (for consumers who may be parsing the frame names). /// - internal abstract class FrameDecoder : IDkmLanguageFrameDecoder + internal abstract class FrameDecoder : IDkmLanguageFrameDecoder + where TCompilation : Compilation + where TMethodSymbol : class, IMethodSymbol + where TModuleSymbol : class, IModuleSymbol + where TTypeSymbol : class, ITypeSymbol + where TTypeParameterSymbol : class, ITypeParameterSymbol { - private readonly InstructionDecoder _instructionDecoder; + private readonly InstructionDecoder _instructionDecoder; - internal FrameDecoder(InstructionDecoder instructionDecoder) + internal FrameDecoder(InstructionDecoder instructionDecoder) { _instructionDecoder = instructionDecoder; } - void IDkmLanguageFrameDecoder.GetFrameName(DkmInspectionContext inspectionContext, DkmWorkList workList, DkmStackWalkFrame frame, DkmVariableInfoFlags argumentFlags, DkmCompletionRoutine completionRoutine) + void IDkmLanguageFrameDecoder.GetFrameName( + DkmInspectionContext inspectionContext, + DkmWorkList workList, + DkmStackWalkFrame frame, + DkmVariableInfoFlags argumentFlags, + DkmCompletionRoutine completionRoutine) { try { Debug.Assert((argumentFlags & (DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types | DkmVariableInfoFlags.Values)) == argumentFlags, "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags); - var instructionAddress = (DkmClrInstructionAddress)frame.InstructionAddress; - var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); - var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); - - if (argumentFlags.Includes(DkmVariableInfoFlags.Values)) - { - // No need to compute the Expandable bit on - // argument values since that can be expensive. - inspectionContext = DkmInspectionContext.Create( - inspectionContext.InspectionSession, - inspectionContext.RuntimeInstance, - inspectionContext.Thread, - inspectionContext.Timeout, - inspectionContext.EvaluationFlags | DkmEvaluationFlags.NoExpansion, - inspectionContext.FuncEvalFlags, - inspectionContext.Radix, - inspectionContext.Language, - inspectionContext.ReturnValue, - inspectionContext.AdditionalVisualizationData, - inspectionContext.AdditionalVisualizationDataPriority, - inspectionContext.ReturnValues); - - // GetFrameArguments returns an array of formatted argument values. We'll pass - // ourselves (GetFrameName) as the continuation of the GetFrameArguments call. - inspectionContext.GetFrameArguments( - workList, - frame, - result => - { - try - { - var builder = ArrayBuilder.GetInstance(); - foreach (var argument in result.Arguments) - { - var evaluatedArgument = argument as DkmSuccessEvaluationResult; - // Not expecting Expandable bit, at least not from this EE. - Debug.Assert((evaluatedArgument == null) || (evaluatedArgument.Flags & DkmEvaluationResultFlags.Expandable) == 0); - builder.Add((evaluatedArgument != null) ? evaluatedArgument.Value : null); - } - - var frameName = _instructionDecoder.GetName(instructionAddress, includeParameterTypes, includeParameterNames, builder); - builder.Free(); - completionRoutine(new DkmGetFrameNameAsyncResult(frameName)); - } - // TODO: Consider calling DkmComponentManager.ReportCurrentNonFatalException() to - // trigger a non-fatal Watson when this occurs. - catch (Exception e) when (!ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) - { - completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e)); - } - finally - { - foreach (var argument in result.Arguments) - { - argument.Close(); - } - } - }); - } - else - { - var frameName = _instructionDecoder.GetName(instructionAddress, includeParameterTypes, includeParameterNames, null); - completionRoutine(new DkmGetFrameNameAsyncResult(frameName)); - } + GetNameWithGenericTypeArguments(inspectionContext, workList, frame, + onSuccess: method => GetFrameName(inspectionContext, workList, frame, argumentFlags, completionRoutine, method), + onFailure: e => completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e))); } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { @@ -106,18 +56,135 @@ void IDkmLanguageFrameDecoder.GetFrameName(DkmInspectionContext inspectionContex } } - void IDkmLanguageFrameDecoder.GetFrameReturnType(DkmInspectionContext inspectionContext, DkmWorkList workList, DkmStackWalkFrame frame, DkmCompletionRoutine completionRoutine) + void IDkmLanguageFrameDecoder.GetFrameReturnType( + DkmInspectionContext inspectionContext, + DkmWorkList workList, + DkmStackWalkFrame frame, + DkmCompletionRoutine completionRoutine) { try { - var returnType = _instructionDecoder.GetReturnType((DkmClrInstructionAddress)frame.InstructionAddress); - var result = new DkmGetFrameReturnTypeAsyncResult(returnType); - completionRoutine(result); + GetNameWithGenericTypeArguments(inspectionContext, workList, frame, + onSuccess: method => completionRoutine(new DkmGetFrameReturnTypeAsyncResult(_instructionDecoder.GetReturnTypeName(method))), + onFailure: e => completionRoutine(DkmGetFrameReturnTypeAsyncResult.CreateErrorResult(e))); } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { throw ExceptionUtilities.Unreachable; } } + + private void GetNameWithGenericTypeArguments( + DkmInspectionContext inspectionContext, + DkmWorkList workList, + DkmStackWalkFrame frame, + Action onSuccess, + Action onFailure) + { + // NOTE: We could always call GetClrGenericParameters, pass them to GetMethod and have that + // return a constructed method symbol, but it seems unwise to call GetClrGenericParameters + // for all frames (as this call requires a round-trip to the debuggee process). + var instructionAddress = (DkmClrInstructionAddress)frame.InstructionAddress; + var compilation = _instructionDecoder.GetCompilation(instructionAddress); + var method = _instructionDecoder.GetMethod(compilation, instructionAddress); + var typeParameters = _instructionDecoder.GetAllTypeParameters(method); + if (!typeParameters.IsEmpty) + { + frame.GetClrGenericParameters( + workList, + result => + { + try + { + var typeArguments = _instructionDecoder.GetTypeSymbols(compilation, method, result.ParameterTypeNames); + if (!typeArguments.IsEmpty) + { + method = _instructionDecoder.ConstructMethod(method, typeParameters, typeArguments); + } + onSuccess(method); + } + catch (Exception e) when (ExpressionEvaluatorFatalError.ReportNonFatalException(e, DkmComponentManager.ReportCurrentNonFatalException)) + { + onFailure(e); + } + }); + } + else + { + onSuccess(method); + } + } + + private void GetFrameName( + DkmInspectionContext inspectionContext, + DkmWorkList workList, + DkmStackWalkFrame frame, + DkmVariableInfoFlags argumentFlags, + DkmCompletionRoutine completionRoutine, + TMethodSymbol method) + { + var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); + var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); + + if (argumentFlags.Includes(DkmVariableInfoFlags.Values)) + { + // No need to compute the Expandable bit on + // argument values since that can be expensive. + inspectionContext = DkmInspectionContext.Create( + inspectionContext.InspectionSession, + inspectionContext.RuntimeInstance, + inspectionContext.Thread, + inspectionContext.Timeout, + inspectionContext.EvaluationFlags | DkmEvaluationFlags.NoExpansion, + inspectionContext.FuncEvalFlags, + inspectionContext.Radix, + inspectionContext.Language, + inspectionContext.ReturnValue, + inspectionContext.AdditionalVisualizationData, + inspectionContext.AdditionalVisualizationDataPriority, + inspectionContext.ReturnValues); + + // GetFrameArguments returns an array of formatted argument values. We'll pass + // ourselves (GetFrameName) as the continuation of the GetFrameArguments call. + inspectionContext.GetFrameArguments( + workList, + frame, + result => + { + var argumentValues = result.Arguments; + try + { + var builder = ArrayBuilder.GetInstance(); + foreach (var argument in argumentValues) + { + var formattedArgument = argument as DkmSuccessEvaluationResult; + // Not expecting Expandable bit, at least not from this EE. + Debug.Assert((formattedArgument == null) || (formattedArgument.Flags & DkmEvaluationResultFlags.Expandable) == 0); + builder.Add(formattedArgument?.Value); + } + + var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder); + builder.Free(); + completionRoutine(new DkmGetFrameNameAsyncResult(frameName)); + } + catch (Exception e) when (ExpressionEvaluatorFatalError.ReportNonFatalException(e, DkmComponentManager.ReportCurrentNonFatalException)) + { + completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e)); + } + finally + { + foreach (var argument in argumentValues) + { + argument.Close(); + } + } + }); + } + else + { + var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, null); + completionRoutine(new DkmGetFrameNameAsyncResult(frameName)); + } + } } } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs index be947e272e0a1..80126b5f5e6bd 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Immutable; using System.Diagnostics; using System.Text; using Microsoft.CodeAnalysis.Collections; @@ -7,13 +8,12 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator { - internal abstract class InstructionDecoder - { - internal abstract string GetName(DkmClrInstructionAddress instructionAddress, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder argumentValues); - internal abstract string GetReturnType(DkmClrInstructionAddress instructionAddress); - } - - internal abstract class InstructionDecoder : InstructionDecoder where TMethodSymbol : class, IMethodSymbol + internal abstract class InstructionDecoder + where TCompilation : Compilation + where TMethodSymbol : class, IMethodSymbol + where TModuleSymbol : class, IModuleSymbol + where TTypeSymbol : class, ITypeSymbol + where TTypeParameterSymbol : class, ITypeParameterSymbol { internal static readonly SymbolDisplayFormat DisplayFormat = new SymbolDisplayFormat( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, @@ -21,21 +21,18 @@ internal abstract class InstructionDecoder : InstructionDecoder w memberOptions: SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeExplicitInterface, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - internal override string GetName(DkmClrInstructionAddress instructionAddress, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder argumentValues) - { - var method = this.GetMethod(instructionAddress); - return this.GetName(method, includeParameterTypes, includeParameterNames, argumentValues); - } + internal abstract void AppendFullName(StringBuilder builder, TMethodSymbol method); - internal override string GetReturnType(DkmClrInstructionAddress instructionAddress) - { - var method = this.GetMethod(instructionAddress); - return method.ReturnType.ToDisplayString(DisplayFormat); - } + /// + /// Constructs a method and any of its generic containing types using the specified . + /// + internal abstract TMethodSymbol ConstructMethod(TMethodSymbol method, ImmutableArray typeParameters, ImmutableArray typeArguments); - internal abstract void AppendFullName(StringBuilder builder, TMethodSymbol method); + internal abstract ImmutableArray GetAllTypeParameters(TMethodSymbol method); + + internal abstract TCompilation GetCompilation(DkmClrInstructionAddress instructionAddress); - internal abstract TMethodSymbol GetMethod(DkmClrInstructionAddress instructionAddress); + internal abstract TMethodSymbol GetMethod(TCompilation compilation, DkmClrInstructionAddress instructionAddress); internal string GetName(TMethodSymbol method, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder argumentValues = null) { @@ -96,5 +93,33 @@ internal string GetName(TMethodSymbol method, bool includeParameterTypes, bool i return pooled.ToStringAndFree(); } + + internal string GetReturnTypeName(TMethodSymbol method) + { + return method.ReturnType.ToDisplayString(DisplayFormat); + } + + internal abstract TypeNameDecoder GetTypeNameDecoder(TCompilation compilation, TMethodSymbol method); + + internal ImmutableArray GetTypeSymbols(TCompilation compilation, TMethodSymbol method, string[] serializedTypeNames) + { + var builder = ArrayBuilder.GetInstance(); + foreach (var name in serializedTypeNames) + { + // The list of type names will include null values if type arguments are not available. + // It seems unlikely that only some type arguments will be missing (and it also seems + // like very little incremental value to include only some of the arguments), so we'll + // keep things simple and omit all type arguments if any are missing. + if (name == null) + { + builder.Free(); + return ImmutableArray.Empty; + } + + var typeNameDecoder = GetTypeNameDecoder(compilation, method); + builder.Add(typeNameDecoder.GetTypeSymbolForSerializedType(name)); + } + return builder.ToImmutableAndFree(); + } } } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs index d5a7b0a357593..bbed5e6088945 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs @@ -17,11 +17,16 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator /// /// This class provides function name information for the Breakpoints window. /// - internal abstract class LanguageInstructionDecoder : IDkmLanguageInstructionDecoder where TMethodSymbol : class, IMethodSymbol + internal abstract class LanguageInstructionDecoder : IDkmLanguageInstructionDecoder + where TCompilation : Compilation + where TMethodSymbol : class, IMethodSymbol + where TModuleSymbol : class, IModuleSymbol + where TTypeSymbol : class, ITypeSymbol + where TTypeParameterSymbol : class, ITypeParameterSymbol { - private readonly InstructionDecoder _instructionDecoder; + private readonly InstructionDecoder _instructionDecoder; - internal LanguageInstructionDecoder(InstructionDecoder instructionDecoder) + internal LanguageInstructionDecoder(InstructionDecoder instructionDecoder) { _instructionDecoder = instructionDecoder; } @@ -37,7 +42,9 @@ string IDkmLanguageInstructionDecoder.GetMethodName(DkmLanguageInstructionAddres Debug.Assert((argumentFlags & (DkmVariableInfoFlags.FullNames | DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)) == argumentFlags, "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags); - var method = _instructionDecoder.GetMethod((DkmClrInstructionAddress)languageInstructionAddress.Address); + var instructionAddress = (DkmClrInstructionAddress)languageInstructionAddress.Address; + var compilation = _instructionDecoder.GetCompilation(instructionAddress); + var method = _instructionDecoder.GetMethod(compilation, instructionAddress); var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb index 1b42c478cc57d..ceb02216d1bbb 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb @@ -1,4 +1,6 @@ -Imports System +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System Imports System.Collections.Immutable Imports System.Runtime.InteropServices Imports System.Threading @@ -173,13 +175,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Return moduleBuilder End Function - Private Shared Function GetAllTypeParameters(method As MethodSymbol) As ImmutableArray(Of TypeParameterSymbol) - Dim builder = ArrayBuilder(Of TypeParameterSymbol).GetInstance() - method.ContainingType.GetAllTypeParameters(builder) - builder.AddRange(method.TypeParameters) - Return builder.ToImmutableAndFree() - End Function - Private Shared Function GetNextMethodName(builder As ArrayBuilder(Of MethodSymbol)) As String ' NOTE: These names are consumed by Concord, so there's no native precedent. Return String.Format("<>m{0}", builder.Count) @@ -242,39 +237,43 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator End If ' Hoisted method parameters (represented as locals in the EE). - Dim ordinal As Integer = 0 If Not _hoistedParameterNames.IsEmpty Then + Dim localIndex As Integer = 0 + For Each local In _localsForBinding ' Since we are showing hoisted method parameters first, the parameters may appear out of order ' in the Locals window if only some of the parameters are hoisted. This is consistent with the ' behavior of the old EE. Dim localName = local.Name If _hoistedParameterNames.Contains(local.Name) Then - AppendLocalAndMethod(localBuilder, methodBuilder, localName, AddressOf Me.GetLocalMethod, container, ordinal, GetLocalResultFlags(local)) + AppendLocalAndMethod(localBuilder, methodBuilder, localName, AddressOf Me.GetLocalMethod, container, localIndex, GetLocalResultFlags(local)) End If - ordinal += 1 + + localIndex += 1 Next End If ' Method parameters (except those that have been hoisted). - ordinal = If(m.IsShared, 0, 1) + Dim parameterIndex = If(m.IsShared, 0, 1) For Each parameter In m.Parameters Dim parameterName = parameter.Name If Not _hoistedParameterNames.Contains(parameterName) Then - AppendLocalAndMethod(localBuilder, methodBuilder, parameterName, AddressOf Me.GetParameterMethod, container, ordinal, DkmClrCompilationResultFlags.None) + AppendLocalAndMethod(localBuilder, methodBuilder, parameterName, AddressOf Me.GetParameterMethod, container, parameterIndex, DkmClrCompilationResultFlags.None) End If - ordinal += 1 + + parameterIndex += 1 Next If Not argumentsOnly Then ' Locals. - ordinal = 0 + Dim localIndex As Integer = 0 For Each local In _localsForBinding Dim localName = local.Name If Not _hoistedParameterNames.Contains(localName) Then - AppendLocalAndMethod(localBuilder, methodBuilder, localName, AddressOf Me.GetLocalMethod, container, ordinal, GetLocalResultFlags(local)) + AppendLocalAndMethod(localBuilder, methodBuilder, localName, AddressOf Me.GetLocalMethod, container, localIndex, GetLocalResultFlags(local)) End If - ordinal += 1 + + localIndex += 1 Next ' "Type variables". @@ -319,7 +318,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator name As String, getMethod As Func(Of EENamedTypeSymbol, String, String, Integer, MethodSymbol), container As EENamedTypeSymbol, - ordinal As Integer, + localOrParameterIndex As Integer, resultFlags As DkmClrCompilationResultFlags) ' Note: The native EE doesn't do this, but if we don't escape keyword identifiers, @@ -327,7 +326,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ' which it can't do correctly without semantic information. name = SyntaxHelpers.EscapeKeywordIdentifiers(name) Dim methodName = GetNextMethodName(methodBuilder) - Dim method = getMethod(container, methodName, name, ordinal) + Dim method = getMethod(container, methodName, name, localOrParameterIndex) localBuilder.Add(New LocalAndMethod(name, methodName, resultFlags)) methodBuilder.Add(method) End Sub @@ -365,27 +364,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator generateMethodBody) End Function - Private Function GetLocalMethod(container As EENamedTypeSymbol, methodName As String, localName As String, index As Integer) As EEMethodSymbol + Private Function GetLocalMethod(container As EENamedTypeSymbol, methodName As String, localName As String, localIndex As Integer) As EEMethodSymbol Dim syntax = SyntaxFactory.IdentifierName(localName) Return Me.CreateMethod( container, methodName, syntax, Function(method, diagnostics) - Dim local = method.LocalsForBinding(index) + Dim local = method.LocalsForBinding(localIndex) Dim expression = New BoundLocal(syntax, local, isLValue:=False, type:=local.Type).MakeCompilerGenerated() Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated() End Function) End Function - Private Function GetParameterMethod(container As EENamedTypeSymbol, methodName As String, parameterName As String, index As Integer) As EEMethodSymbol + Private Function GetParameterMethod(container As EENamedTypeSymbol, methodName As String, parameterName As String, parameterIndex As Integer) As EEMethodSymbol Dim syntax = SyntaxFactory.IdentifierName(parameterName) Return Me.CreateMethod( container, methodName, syntax, Function(method, diagnostics) - Dim parameter = method.Parameters(index) + Dim parameter = method.Parameters(parameterIndex) Dim expression = New BoundParameter(syntax, parameter, isLValue:=False, type:=parameter.Type).MakeCompilerGenerated() Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated() End Function) diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb index 4c6b96d59311e..b2f418a0b8278 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb @@ -1,4 +1,7 @@ -Imports System.Runtime.CompilerServices +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports System.Runtime.CompilerServices Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator @@ -59,5 +62,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Friend Function IsStateMachineType(type As TypeSymbol) As Boolean Return type.Name.StartsWith(StringConstants.StateMachineTypeNamePrefix, StringComparison.Ordinal) End Function + + + Friend Function GetAllTypeParameters(method As MethodSymbol) As ImmutableArray(Of TypeParameterSymbol) + Dim builder = ArrayBuilder(Of TypeParameterSymbol).GetInstance() + method.ContainingType.GetAllTypeParameters(builder) + builder.AddRange(method.TypeParameters) + Return builder.ToImmutableAndFree() + End Function End Module End Namespace diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb index fc1aa18494b38..dc71a62a05c4a 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb @@ -1,9 +1,13 @@ -Imports Microsoft.CodeAnalysis.ExpressionEvaluator +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.ExpressionEvaluator +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator - Friend NotInheritable Class VisualBasicFrameDecoder : Inherits FrameDecoder + Friend NotInheritable Class VisualBasicFrameDecoder : Inherits FrameDecoder(Of VisualBasicCompilation, MethodSymbol, PEModuleSymbol, TypeSymbol, TypeParameterSymbol) Public Sub New() MyBase.New(VisualBasicInstructionDecoder.Instance) diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb index ba82c0aac338b..c4fac085d08f5 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb @@ -1,6 +1,8 @@ -Imports System.Runtime.InteropServices +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.ExpressionEvaluator +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Imports Microsoft.VisualStudio.Debugger Imports Microsoft.VisualStudio.Debugger.Clr @@ -8,7 +10,7 @@ Imports System.Text Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator - Friend NotInheritable Class VisualBasicInstructionDecoder : Inherits InstructionDecoder(Of PEMethodSymbol) + Friend NotInheritable Class VisualBasicInstructionDecoder : Inherits InstructionDecoder(Of VisualBasicCompilation, MethodSymbol, PEModuleSymbol, TypeSymbol, TypeParameterSymbol) ' These strings were not localized in the old EE. We'll keep them that way ' so as not to break consumers who may have been parsing frame names... @@ -18,12 +20,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ''' ''' Singleton instance of (created using default constructor). ''' - Friend Shared ReadOnly Instance as VisualBasicInstructionDecoder = New VisualBasicInstructionDecoder() + Friend Shared ReadOnly Instance As VisualBasicInstructionDecoder = New VisualBasicInstructionDecoder() Private Sub New() End Sub - Friend Overrides Sub AppendFullName(builder As StringBuilder, method As PEMethodSymbol) + Friend Overrides Sub AppendFullName(builder As StringBuilder, method As MethodSymbol) Dim parts = method.ToDisplayParts(DisplayFormat) Dim numParts = parts.Length For i = 0 To numParts - 1 @@ -59,9 +61,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Next End Sub - Friend Overrides Function GetMethod(instructionAddress As DkmClrInstructionAddress) As PEMethodSymbol - Dim moduleInstance = instructionAddress.ModuleInstance - Dim appDomain = moduleInstance.AppDomain + Friend Overrides Function ConstructMethod(method As MethodSymbol, typeParameters As ImmutableArray(Of TypeParameterSymbol), typeArguments As ImmutableArray(Of TypeSymbol)) As MethodSymbol + Dim methodArity = method.Arity + Dim methodArgumentStartIndex = typeParameters.Length - methodArity + Dim typeMap = TypeSubstitution.Create( + method, + ImmutableArray.Create(typeParameters, 0, methodArgumentStartIndex), + ImmutableArray.Create(typeArguments, 0, methodArgumentStartIndex)) + Dim substitutedType = typeMap.SubstituteNamedType(method.ContainingType) + method = method.AsMember(substitutedType) + If methodArity > 0 Then + method = method.Construct(ImmutableArray.Create(typeArguments, methodArgumentStartIndex, methodArity)) + End If + Return method + End Function + + Friend Overrides Function GetAllTypeParameters(method As MethodSymbol) As ImmutableArray(Of TypeParameterSymbol) + Return method.GetAllTypeParameters() + End Function + + Friend Overrides Function GetCompilation(instructionAddress As DkmClrInstructionAddress) As VisualBasicCompilation + Dim appDomain = instructionAddress.ModuleInstance.AppDomain Dim previous = appDomain.GetDataItem(Of VisualBasicMetadataContext)() Dim metadataBlocks = instructionAddress.Process.GetMetadataBlocks(appDomain) @@ -73,7 +93,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator appDomain.SetDataItem(DkmDataCreationDisposition.CreateAlways, New VisualBasicMetadataContext(metadataBlocks)) End If - Return compilation.GetSourceMethod(moduleInstance.Mvid, instructionAddress.MethodId.Token) + Return compilation + End Function + + Friend Overrides Function GetMethod(compilation As VisualBasicCompilation, instructionAddress As DkmClrInstructionAddress) As MethodSymbol + Return compilation.GetSourceMethod(instructionAddress.ModuleInstance.Mvid, instructionAddress.MethodId.Token) + End Function + + Friend Overrides Function GetTypeNameDecoder(compilation As VisualBasicCompilation, method As MethodSymbol) As TypeNameDecoder(Of PEModuleSymbol, TypeSymbol) + Debug.Assert(TypeOf method Is PEMethodSymbol) + Return New EETypeNameDecoder(compilation, DirectCast(method.ContainingModule, PEModuleSymbol)) End Function End Class diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb index cd4ba00673d41..c8fad9add4aa5 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb @@ -1,10 +1,13 @@ -Imports Microsoft.CodeAnalysis.ExpressionEvaluator +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.ExpressionEvaluator +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator - Friend NotInheritable Class VisualBasicLanguageInstructionDecoder : Inherits LanguageInstructionDecoder(Of PEMethodSymbol) + Friend NotInheritable Class VisualBasicLanguageInstructionDecoder : Inherits LanguageInstructionDecoder(Of VisualBasicCompilation, MethodSymbol, PEModuleSymbol, TypeSymbol, TypeParameterSymbol) Public Sub New() MyBase.New(VisualBasicInstructionDecoder.Instance) diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb index 5b0f03b1db133..ccc81e7fa618e 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb @@ -1,4 +1,6 @@ -Imports System.Collections.Immutable +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Collections.Immutable Imports System.Globalization Imports System.Reflection.Metadata Imports System.Threading diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb index 1b33fa3b22a4f..93796e17f3f75 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb @@ -1,4 +1,7 @@ -Imports System.Reflection.Metadata.Ecma335 +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Reflection.Metadata.Ecma335 +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Imports Microsoft.CodeAnalysis.ExpressionEvaluator Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests @@ -20,11 +23,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator '// TODO: string argument values requiring quotes '// TODO: argument flags == names only, types only, values only '// TODO: params Argument values - '// TODO: GetFrameReturnType primitive types - '// TODO: GetFrameReturnType non-primitive types (nested namespace/class) - '// TODO: GetFrameReturnType generic(Of non-primitive, nested) - '// TODO: GetFrameReturnType generic(Of generic) - '// TODO: GetFrameReturnType generic(Of primitive) + '// TODO: generic class/method with 2 or more type parameters + '// TODO: generic argument type that is not from a referenced assembly Public Class InstructionDecoderTests : Inherits ExpressionCompilerTestBase @@ -85,7 +85,6 @@ Class Class1(Of T) Sub M3(Of U)(a As Action(Of U)) End Sub End Class" - ' TODO: Type parameters should be substituted with type arguments once we have an API to retrieve them. Assert.Equal( "Class1(Of T).M1(Of U)(System.Action(Of Integer) a)", @@ -98,6 +97,52 @@ End Class" Assert.Equal( "Class1(Of T).M3(Of U)(System.Action(Of U) a)", GetName(source, "Class1.M3", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types)) + + Assert.Equal( + "Class1(Of String).M1(Of Decimal)(System.Action(Of Integer) a)", + GetName(source, "Class1.M1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)})) + + Assert.Equal( + "Class1(Of String).M2(Of Decimal)(System.Action(Of String) a)", + GetName(source, "Class1.M2", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)})) + + Assert.Equal( + "Class1(Of String).M3(Of Decimal)(System.Action(Of Decimal) a)", + GetName(source, "Class1.M3", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)})) + End Sub + + + Sub GetNameNullTypeArguments() + Dim source = " +Imports System +Class Class1(Of T) + Sub M(Of U)(a As Action(Of U)) + End Sub +End Class" + + Assert.Equal( + "Class1(Of T).M(Of U)(System.Action(Of U) a)", + GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:=New Type() {Nothing, Nothing})) + + Assert.Equal( + "Class1(Of T).M(Of U)(System.Action(Of U) a)", + GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), Nothing})) + + Assert.Equal( + "Class1(Of T).M(Of U)(System.Action(Of U) a)", + GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={Nothing, GetType(Decimal)})) + End Sub + + + Sub GetNameGenericArgumentTypeNotInReferences() + Dim source = " +Class Class1 +End Class" + + Dim serializedTypeArgumentName = "Class1, " & NameOf(InstructionDecoderTests) & ", Culture=neutral, PublicKeyToken=null" + Assert.Equal( + "System.Collections.Generic.Comparer(Of Class1).Create(System.Comparison(Of Class1) comparison)", + GetName(source, "System.Collections.Generic.Comparer.Create", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={serializedTypeArgumentName})) End Sub @@ -130,8 +175,8 @@ Class C End Class" Assert.Equal( - "C.M(Of T)(T x)", - GetName(source, "C.VB$StateMachine_1_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types)) + "C.M(Of Long)(Long x)", + GetName(source, "C.VB$StateMachine_1_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(Long)})) End Sub @@ -175,10 +220,10 @@ Class Class1(Of T) Dim f As Func(Of U, T) = Function(u2 As U) u2 End Sub End Class" - ' TODO: Type parameter $CLS0 should be substituted with a type argument once we have an API to retrieve it. + Assert.Equal( - "Class1(Of T)..($CLS0 u2)", - GetName(source, "Class1._Closure$__1._Lambda$__1-1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types)) + "Class1(Of System.Exception)..(System.ArgumentException u2)", + GetName(source, "Class1._Closure$__1._Lambda$__1-1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(Exception), GetType(ArgumentException)})) End Sub @@ -196,7 +241,7 @@ End Module" Assert.Equal( "Module1.M(Date d = #6/23/1912#)", - GetName(source, "Module1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, "#6/23/1912#")) + GetName(source, "Module1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, argumentValues:={"#6/23/1912#"})) End Sub @@ -284,14 +329,120 @@ End Module" GetName(source, "Module1.M2", DkmVariableInfoFlags.None)) End Sub - Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, ParamArray argumentValues() As String) As String + + Sub GetReturnTypeNamePrimitive() + Dim source = " +Class C + Function M1() As UInteger + Return 42 + End Function +End Class" + + Assert.Equal("UInteger", GetReturnTypeName(source, "C.M1")) + End Sub + + + Sub GetReturnTypeNameNested() + Dim source = " +Class C + Function M1() As N.D.E + Return Nothing + End Function +End Class +Namespace N + Class D + Friend Structure E + End Structure + End Class +End Namespace" + + Assert.Equal("N.D.E", GetReturnTypeName(source, "C.M1")) + End Sub + + + Sub GetReturnTypeNameGenericOfPrimitive() + Dim source = " +Imports System +Class C + Function M1() As Action(Of Int32) + Return Nothing + End Function +End Class" + + Assert.Equal("System.Action(Of Integer)", GetReturnTypeName(source, "C.M1")) + End Sub + + + Sub GetReturnTypeNameGenericOfNested() + Dim source = " +Imports System +Class C + Function M1() As Action(Of D) + Return Nothing + End Function + Class D + End Class +End Class" + + Assert.Equal("System.Action(Of C.D)", GetReturnTypeName(source, "C.M1")) + End Sub + + + Sub GetReturnTypeNameGenericOfGeneric() + Dim source = " +Imports System +Class C + Function M1(Of T)() As Action(Of Func(Of T)) + Return Nothing + End Function +End Class" + + Assert.Equal("System.Action(Of System.Func(Of Object))", GetReturnTypeName(source, "C.M1", typeArguments:={GetType(Object)})) + End Sub + + Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, Optional typeArguments() As Type = Nothing, Optional argumentValues() As String = Nothing) As String + Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray() + Return GetName(source, methodName, argumentFlags, serializedTypeArgumentNames, argumentValues) + End Function + + Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, typeArguments() As String, Optional argumentValues() As String = Nothing) As String Debug.Assert((argumentFlags And (DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types)) = argumentFlags, "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags) + Dim instructionDecoder = VisualBasicInstructionDecoder.Instance + Dim method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder) + + Dim includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types) + Dim includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names) + Dim builder As ArrayBuilder(Of String) = Nothing + If argumentValues IsNot Nothing Then + Assert.InRange(argumentValues.Length, 1, Integer.MaxValue) + builder = ArrayBuilder(Of String).GetInstance() + builder.AddRange(argumentValues) + End If + + Dim name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder) + If builder IsNot Nothing Then + builder.Free() + End If + + Return name + End Function + + Private Function GetReturnTypeName(source As String, methodName As String, Optional typeArguments() As Type = Nothing) As String + Dim instructionDecoder = VisualBasicInstructionDecoder.Instance + Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray() + Dim method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder) + + Return instructionDecoder.GetReturnTypeName(method) + End Function + + Private Function GetConstructedMethod(source As String, methodName As String, serializedTypeArgumentNames() As String, instructionDecoder As VisualBasicInstructionDecoder) As MethodSymbol Dim compilation = CreateCompilationWithReferences( {VisualBasicSyntaxTree.ParseText(source)}, references:={MscorlibRef_v4_0_30316_17626, MsvbRef_v4_0_30319_17929}, - options:=TestOptions.DebugDll) + options:=TestOptions.DebugDll, + assemblyName:=NameOf(InstructionDecoderTests)) Dim runtime = CreateRuntimeInstance(compilation) Dim moduleInstances = runtime.Modules Dim blocks = moduleInstances.SelectAsArray(Function(m) m.MetadataBlock) @@ -301,26 +452,24 @@ End Module" ' Once we have the method token, we want to look up the method (again) ' using the same helper as the product code. This helper will also map ' async/ iterator "MoveNext" methods to the original source method. - Dim method = compilation.GetSourceMethod( + Dim method As MethodSymbol = compilation.GetSourceMethod( DirectCast(frame.ContainingModule, PEModuleSymbol).Module.GetModuleVersionIdOrThrow(), MetadataTokens.GetToken(frame.Handle)) - Dim includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types) - Dim includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names) - Dim builder As ArrayBuilder(Of String) = Nothing - If argumentValues.Length > 0 Then - builder = ArrayBuilder(Of String).GetInstance() - builder.AddRange(argumentValues) - End If - - Dim frameDecoder = VisualBasicInstructionDecoder.Instance - Dim frameName = frameDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder) - If builder IsNot Nothing Then - builder.Free() + If serializedTypeArgumentNames IsNot Nothing Then + Assert.NotEmpty(serializedTypeArgumentNames) + Dim typeParameters = instructionDecoder.GetAllTypeParameters(method) + Assert.NotEmpty(typeParameters) + Dim typeNameDecoder = New EETypeNameDecoder(compilation, DirectCast(method.ContainingModule, PEModuleSymbol)) + ' Use the same helper method as the FrameDecoder to get the TypeSymbols for the + ' generic type arguments (rather than using EETypeNameDecoder directly). + Dim typeArgumentSymbols = instructionDecoder.GetTypeSymbols(compilation, method, serializedTypeArgumentNames) + If Not typeArgumentSymbols.IsEmpty Then + method = instructionDecoder.ConstructMethod(method, typeParameters, typeArgumentSymbols) + End If End If - Return frameName + Return method End Function - End Class End Namespace diff --git a/src/Roslyn.sln b/src/Roslyn.sln index 3f18096f8fc4d..f74ff18e0d91e 100644 --- a/src/Roslyn.sln +++ b/src/Roslyn.sln @@ -285,6 +285,28 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics", "Diagnostics" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetadataVisualizer", "Tools\Source\MetadataVisualizer\MetadataVisualizer.csproj", "{4C7847DB-C412-4D5E-B573-F12FA0A76127}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeAnalysis", "CodeAnalysis", "{2344BE45-7F6B-4A4E-9418-567FA2D9CA8C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysisDiagnosticAnalyzers", "Diagnostics\CodeAnalysis\Core\CodeAnalysisDiagnosticAnalyzers.csproj", "{D8762A0A-3832-47BE-BCF6-8B1060BE6B28}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpCodeAnalysisDiagnosticAnalyzers", "Diagnostics\CodeAnalysis\CSharp\CSharpCodeAnalysisDiagnosticAnalyzers.csproj", "{921B412A-5551-4853-82B4-46AD5A05A03E}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BasicCodeAnalysisDiagnosticAnalyzers", "Diagnostics\CodeAnalysis\VisualBasic\BasicCodeAnalysisDiagnosticAnalyzers.vbproj", "{B1A6A74B-E484-48FB-8745-7A30A06DB631}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysisDiagnosticAnalyzersTest", "Diagnostics\CodeAnalysis\Test\CodeAnalysisDiagnosticAnalyzersTest.csproj", "{0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysisDiagnosticsSetup", "Diagnostics\CodeAnalysis\Setup\CodeAnalysisDiagnosticsSetup.csproj", "{54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "System.Runtime.Analyzers", "System.Runtime.Analyzers", "{F24D89AC-5A93-4F21-A329-DCEFD41EC0FE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemRuntimeAnalyzers", "Diagnostics\FxCop\System.Runtime.Analyzers\Core\SystemRuntimeAnalyzers.csproj", "{BAA0FEE4-93C8-46F0-BB36-53A6053776C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpSystemRuntimeAnalyzers", "Diagnostics\FxCop\System.Runtime.Analyzers\CSharp\CSharpSystemRuntimeAnalyzers.csproj", "{A36451EC-1127-40CE-B841-47F393D24624}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BasicSystemRuntimeAnalyzers", "Diagnostics\FxCop\System.Runtime.Analyzers\VisualBasic\BasicSystemRuntimeAnalyzers.vbproj", "{D835C05E-9D83-40B2-9D25-19EB652F10D7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemRuntimeAnalyzersTest", "Diagnostics\FxCop\System.Runtime.Analyzers\Test\SystemRuntimeAnalyzersTest.csproj", "{0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{edc68a0e-c68d-4a74-91b7-bf38ec909888}*SharedItemsImports = 4 @@ -1246,6 +1268,60 @@ Global {4C7847DB-C412-4D5E-B573-F12FA0A76127}.Release|Any CPU.Build.0 = Release|Any CPU {4C7847DB-C412-4D5E-B573-F12FA0A76127}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {4C7847DB-C412-4D5E-B573-F12FA0A76127}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {921B412A-5551-4853-82B4-46AD5A05A03E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {921B412A-5551-4853-82B4-46AD5A05A03E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {921B412A-5551-4853-82B4-46AD5A05A03E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {921B412A-5551-4853-82B4-46AD5A05A03E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {921B412A-5551-4853-82B4-46AD5A05A03E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {921B412A-5551-4853-82B4-46AD5A05A03E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B1A6A74B-E484-48FB-8745-7A30A06DB631}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1A6A74B-E484-48FB-8745-7A30A06DB631}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B1A6A74B-E484-48FB-8745-7A30A06DB631}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B1A6A74B-E484-48FB-8745-7A30A06DB631}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1A6A74B-E484-48FB-8745-7A30A06DB631}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B1A6A74B-E484-48FB-8745-7A30A06DB631}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A36451EC-1127-40CE-B841-47F393D24624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A36451EC-1127-40CE-B841-47F393D24624}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A36451EC-1127-40CE-B841-47F393D24624}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A36451EC-1127-40CE-B841-47F393D24624}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A36451EC-1127-40CE-B841-47F393D24624}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A36451EC-1127-40CE-B841-47F393D24624}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D835C05E-9D83-40B2-9D25-19EB652F10D7}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1375,6 +1451,16 @@ Global {5002636A-FE8D-40BF-8818-AB513A2194FA} = {235A3418-A3B0-4844-BCEB-F1CF45069232} {ABDBAC1E-350E-4DC3-BB45-3504404545EE} = {235A3418-A3B0-4844-BCEB-F1CF45069232} {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0} = {235A3418-A3B0-4844-BCEB-F1CF45069232} - {4C7847DB-C412-4D5E-B573-F12FA0A76127} = {64BDF58B-41BA-A19E-0D34-B5FA598403B6} + {2344BE45-7F6B-4A4E-9418-567FA2D9CA8C} = {5F5DD61A-746D-40AE-A89C-EF82B39C036E} + {D8762A0A-3832-47BE-BCF6-8B1060BE6B28} = {2344BE45-7F6B-4A4E-9418-567FA2D9CA8C} + {921B412A-5551-4853-82B4-46AD5A05A03E} = {2344BE45-7F6B-4A4E-9418-567FA2D9CA8C} + {B1A6A74B-E484-48FB-8745-7A30A06DB631} = {2344BE45-7F6B-4A4E-9418-567FA2D9CA8C} + {0C2925AD-CD97-46FA-A686-E2C1AD19DAD8} = {2344BE45-7F6B-4A4E-9418-567FA2D9CA8C} + {54F6AE18-B0CD-4799-9DF0-9B1AAD6A78AF} = {2344BE45-7F6B-4A4E-9418-567FA2D9CA8C} + {F24D89AC-5A93-4F21-A329-DCEFD41EC0FE} = {24E8CBFA-38D2-486F-B772-C10AB2DC7F01} + {BAA0FEE4-93C8-46F0-BB36-53A6053776C8} = {F24D89AC-5A93-4F21-A329-DCEFD41EC0FE} + {A36451EC-1127-40CE-B841-47F393D24624} = {F24D89AC-5A93-4F21-A329-DCEFD41EC0FE} + {D835C05E-9D83-40B2-9D25-19EB652F10D7} = {F24D89AC-5A93-4F21-A329-DCEFD41EC0FE} + {0FAE8CB3-4D2F-4A11-B1E6-F47EFF0FB863} = {F24D89AC-5A93-4F21-A329-DCEFD41EC0FE} EndGlobalSection EndGlobal diff --git a/src/Test/PdbUtilities/Metadata/MetadataReaderPdbExtensions.cs b/src/Test/PdbUtilities/Metadata/MetadataReaderPdbExtensions.cs index 5eed315af4f8c..7bf8727f158bb 100644 --- a/src/Test/PdbUtilities/Metadata/MetadataReaderPdbExtensions.cs +++ b/src/Test/PdbUtilities/Metadata/MetadataReaderPdbExtensions.cs @@ -4,9 +4,11 @@ using System.Diagnostics; using System.Reflection.Metadata.Ecma335; +// TODO: to be moved to System.Reflection.Metadata. + namespace System.Reflection.Metadata { - public enum ImportScopeKind + internal enum ImportScopeKind { ImportNamespace = 1, ImportAssemblyNamespace = 2, @@ -19,7 +21,7 @@ public enum ImportScopeKind AliasType = 9 } - public struct ImportDefinition + internal struct ImportDefinition { private readonly ImportScopeKind _kind; private readonly BlobHandle _alias; @@ -28,8 +30,8 @@ public struct ImportDefinition internal ImportDefinition( ImportScopeKind kind, - BlobHandle alias = default(BlobHandle), - AssemblyReferenceHandle assembly = default(AssemblyReferenceHandle), + BlobHandle alias = default(BlobHandle), + AssemblyReferenceHandle assembly = default(AssemblyReferenceHandle), Handle typeOrNamespace = default(Handle)) { Debug.Assert( diff --git a/src/Tools/Source/RunTests/TestRunner.cs b/src/Tools/Source/RunTests/TestRunner.cs index b18c3c0a65752..48b64d223ce33 100644 --- a/src/Tools/Source/RunTests/TestRunner.cs +++ b/src/Tools/Source/RunTests/TestRunner.cs @@ -129,8 +129,8 @@ private async Task RunTest(string assemblyPath) var all = File.ReadAllText(resultsPath).Trim(); if (all.Length == 0) { - var output = processOutput.OutputLines.Concat(processOutput.ErrorLines).Aggregate((x, y) => x + Environment.NewLine + y); - File.WriteAllText(resultsPath, output); + var output = processOutput.OutputLines.Concat(processOutput.ErrorLines).ToArray(); + File.WriteAllLines(resultsPath, output); } errorOutput = processOutput.ErrorLines.Aggregate((x, y) => x + Environment.NewLine + y); diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs b/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs index d84ea05d3c586..c24f3bca84032 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs @@ -94,12 +94,12 @@ public static void InsertMembers(this SyntaxEditor editor, SyntaxNode declaratio public static void AddInterfaceType(this SyntaxEditor editor, SyntaxNode declaration, SyntaxNode interfaceType) { - editor.ReplaceNode(declaration, (d, g) => g.AddInterfaceType(declaration, interfaceType)); + editor.ReplaceNode(declaration, (d, g) => g.AddInterfaceType(d, interfaceType)); } public static void AddBaseType(this SyntaxEditor editor, SyntaxNode declaration, SyntaxNode baseType) { - editor.ReplaceNode(declaration, (d, g) => g.AddBaseType(declaration, baseType)); + editor.ReplaceNode(declaration, (d, g) => g.AddBaseType(d, baseType)); } } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/CommonFormattingHelpers.cs b/src/Workspaces/Core/Portable/Shared/Utilities/CommonFormattingHelpers.cs index d3af0c3551fce..cd6ca2f63b19c 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/CommonFormattingHelpers.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/CommonFormattingHelpers.cs @@ -149,36 +149,6 @@ public static int GetTokenColumn(this SyntaxTree tree, SyntaxToken token, int ta return line.GetColumnFromLineOffset(startPosition - line.Start, tabSize); } - public static bool IsFirstTokenOnLine(this SyntaxTree tree, SyntaxToken token) - { - Contract.ThrowIfNull(tree); - Contract.ThrowIfTrue(token.RawKind == 0); - - var previousToken = token.GetPreviousToken(); - - // there should be only whitespace between two tokens unless the token can't be the first token on line - if (!string.IsNullOrWhiteSpace(tree.GetText().GetText(previousToken, token))) - { - return false; - } - - if (previousToken.RawKind == 0) - { - return true; - } - - var previousLine = tree.GetText().Lines.GetLineFromPosition(previousToken.Span.End); - var lineNumber = tree.GetText().Lines.IndexOf(token.SpanStart); - - // if span.End is at the edge of two lines, it belongs to previous line - if (previousLine.Start == previousToken.Span.End) - { - return previousLine.LineNumber - 1 < lineNumber; - } - - return previousLine.LineNumber < lineNumber; - } - public static string GetText(this SourceText text, SyntaxToken token1, SyntaxToken token2) { return (token1.RawKind == 0) ? text.ToString(TextSpan.FromBounds(0, token2.SpanStart)) : text.ToString(TextSpan.FromBounds(token1.Span.End, token2.SpanStart)); diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb index ca58e72f1268f..db3d379a3e793 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb @@ -880,8 +880,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic name = moduleDecl.ModuleStatement.Identifier.ValueText typeParameterList = moduleDecl.ModuleStatement.TypeParameterList Case SyntaxKind.NamespaceBlock - Return GetNodeName(CType(node, NamespaceBlockSyntax).NamespaceStatement.Name, includeTypeParameters:=False) - typeParameterList = Nothing + Dim nameSyntax = CType(node, NamespaceBlockSyntax).NamespaceStatement.Name + If nameSyntax.Kind() = SyntaxKind.GlobalName Then + Return Nothing + Else + Return GetNodeName(nameSyntax, includeTypeParameters:=False) + End If Case SyntaxKind.QualifiedName Dim qualified = CType(node, QualifiedNameSyntax) If qualified.Left.Kind() = SyntaxKind.GlobalName Then