ICodeDefinitionFactoryExtensions.cs 18.5 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGeneration;
8
using Microsoft.CodeAnalysis.Editing;
P
Pilchie 已提交
9
using Microsoft.CodeAnalysis.FindSymbols;
10
using Microsoft.CodeAnalysis.Simplification;
P
Pilchie 已提交
11 12 13 14 15 16 17
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Shared.Extensions
{
    internal static partial class ICodeDefinitionFactoryExtensions
    {
        public static SyntaxNode CreateThrowNotImplementStatement(
18
            this SyntaxGenerator codeDefinitionFactory,
P
Pilchie 已提交
19 20
            Compilation compilation)
        {
21 22
            return codeDefinitionFactory.ThrowStatement(
                codeDefinitionFactory.ObjectCreationExpression(
P
Pilchie 已提交
23 24 25 26 27
                    compilation.NotImplementedExceptionType(),
                    SpecializedCollections.EmptyList<SyntaxNode>()));
        }

        public static IList<SyntaxNode> CreateThrowNotImplementedStatementBlock(
28
            this SyntaxGenerator codeDefinitionFactory,
P
Pilchie 已提交
29 30 31 32 33 34
            Compilation compilation)
        {
            return new[] { CreateThrowNotImplementStatement(codeDefinitionFactory, compilation) };
        }

        public static IList<SyntaxNode> CreateArguments(
35
            this SyntaxGenerator factory,
P
Pilchie 已提交
36 37 38 39 40 41
            ImmutableArray<IParameterSymbol> parameters)
        {
            return parameters.Select(p => CreateArgument(factory, p)).ToList();
        }

        private static SyntaxNode CreateArgument(
42
            this SyntaxGenerator factory,
P
Pilchie 已提交
43 44
            IParameterSymbol parameter)
        {
45
            return factory.Argument(parameter.RefKind, factory.IdentifierName(parameter.Name));
P
Pilchie 已提交
46 47 48
        }

        public static IMethodSymbol CreateBaseDelegatingConstructor(
49
            this SyntaxGenerator factory,
P
Pilchie 已提交
50 51 52 53 54 55 56 57
            IMethodSymbol constructor,
            string typeName)
        {
            // Create a constructor that calls the base constructor.  Note: if there are no
            // parameters then don't bother writing out "base()" it's automatically implied.
            return CodeGenerationSymbolFactory.CreateConstructorSymbol(
                attributes: null,
                accessibility: Accessibility.Public,
58
                modifiers: new DeclarationModifiers(),
P
Pilchie 已提交
59 60 61 62 63 64 65
                typeName: typeName,
                parameters: constructor.Parameters,
                statements: null,
                baseConstructorArguments: constructor.Parameters.Length == 0 ? null : factory.CreateArguments(constructor.Parameters));
        }

        public static IEnumerable<ISymbol> CreateFieldDelegatingConstructor(
66
            this SyntaxGenerator factory,
P
Pilchie 已提交
67 68 69 70 71 72 73 74
            string typeName,
            INamedTypeSymbol containingTypeOpt,
            IList<IParameterSymbol> parameters,
            IDictionary<string, ISymbol> parameterToExistingFieldMap,
            IDictionary<string, string> parameterToNewFieldMap,
            CancellationToken cancellationToken)
        {
            var fields = factory.CreateFieldsForParameters(parameters, parameterToNewFieldMap);
75 76
            var statements = factory.CreateAssignmentStatements(parameters, parameterToExistingFieldMap, parameterToNewFieldMap)
                                    .Select(s => s.WithAdditionalAnnotations(Simplifier.Annotation));
P
Pilchie 已提交
77 78 79 80 81 82 83 84 85

            foreach (var field in fields)
            {
                yield return field;
            }

            yield return CodeGenerationSymbolFactory.CreateConstructorSymbol(
                attributes: null,
                accessibility: Accessibility.Public,
86
                modifiers: new DeclarationModifiers(),
P
Pilchie 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
                typeName: typeName,
                parameters: parameters,
                statements: statements.ToList(),
                thisConstructorArguments: GetThisConstructorArguments(containingTypeOpt, parameterToExistingFieldMap));
        }

        private static IList<SyntaxNode> GetThisConstructorArguments(
            INamedTypeSymbol containingTypeOpt,
            IDictionary<string, ISymbol> parameterToExistingFieldMap)
        {
            if (containingTypeOpt != null && containingTypeOpt.TypeKind == TypeKind.Struct)
            {
                // Special case.  If we're generating a struct constructor, then we'll need
                // to initialize all fields in the struct, not just the ones we're creating.  To
                // do that, we call the default constructor.
                var realFields = containingTypeOpt.GetMembers()
                                     .OfType<IFieldSymbol>()
                                     .Where(f => !f.IsStatic);
                var initializedFields = parameterToExistingFieldMap.Values
                                            .OfType<IFieldSymbol>()
                                            .Where(f => !f.IsImplicitlyDeclared && !f.IsStatic);
                if (initializedFields.Count() < realFields.Count())
                {
                    // We have less field assignments than actual fields.  Generate a call to the
                    // default constructor as well.
                    return new List<SyntaxNode>();
                }
            }

            return null;
        }

        public static IEnumerable<IFieldSymbol> CreateFieldsForParameters(
120
            this SyntaxGenerator factory,
P
Pilchie 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
            IList<IParameterSymbol> parameters,
            IDictionary<string, string> parameterToNewFieldMap)
        {
            foreach (var parameter in parameters)
            {
                var refKind = parameter.RefKind;
                var parameterType = parameter.Type;
                var parameterName = parameter.Name;

                if (refKind != RefKind.Out)
                {
                    // For non-out parameters, create a field and assign the parameter to it. 
                    // TODO: I'm not sure that's what we really want for ref parameters. 
                    string fieldName;
                    if (TryGetValue(parameterToNewFieldMap, parameterName, out fieldName))
                    {
                        yield return CodeGenerationSymbolFactory.CreateFieldSymbol(
                            attributes: null,
                            accessibility: Accessibility.Private,
140
                            modifiers: default(DeclarationModifiers),
P
Pilchie 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
                            type: parameterType,
                            name: parameterToNewFieldMap[parameterName]);
                    }
                }
            }
        }

        private static bool TryGetValue(IDictionary<string, string> dictionary, string key, out string value)
        {
            value = null;
            return
                dictionary != null &&
                dictionary.TryGetValue(key, out value);
        }

        private static bool TryGetValue(IDictionary<string, ISymbol> dictionary, string key, out string value)
        {
            value = null;
            ISymbol symbol;
            if (dictionary != null && dictionary.TryGetValue(key, out symbol))
            {
                value = symbol.Name;
                return true;
            }

            return false;
        }

        public static IEnumerable<SyntaxNode> CreateAssignmentStatements(
170
            this SyntaxGenerator factory,
P
Pilchie 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184
            IList<IParameterSymbol> parameters,
            IDictionary<string, ISymbol> parameterToExistingFieldMap,
            IDictionary<string, string> parameterToNewFieldMap)
        {
            foreach (var parameter in parameters)
            {
                var refKind = parameter.RefKind;
                var parameterType = parameter.Type;
                var parameterName = parameter.Name;

                if (refKind == RefKind.Out)
                {
                    // If it's an out param, then don't create a field for it.  Instead, assign
                    // assign the default value for that type (i.e. "default(...)") to it.
185 186 187 188
                    var assignExpression = factory.AssignmentStatement(
                        factory.IdentifierName(parameterName),
                        factory.DefaultExpression(parameterType));
                    var statement = factory.ExpressionStatement(assignExpression);
P
Pilchie 已提交
189 190 191 192 193 194 195 196 197 198
                    yield return statement;
                }
                else
                {
                    // For non-out parameters, create a field and assign the parameter to it. 
                    // TODO: I'm not sure that's what we really want for ref parameters. 
                    string fieldName;
                    if (TryGetValue(parameterToExistingFieldMap, parameterName, out fieldName) ||
                        TryGetValue(parameterToNewFieldMap, parameterName, out fieldName))
                    {
199 200 201 202 203 204
                        var assignExpression = factory.AssignmentStatement(
                            factory.MemberAccessExpression(
                                factory.ThisExpression(),
                                factory.IdentifierName(fieldName)),
                            factory.IdentifierName(parameterName));
                        var statement = factory.ExpressionStatement(assignExpression);
P
Pilchie 已提交
205 206 207 208 209 210 211
                        yield return statement;
                    }
                }
            }
        }

        public static IPropertySymbol OverrideProperty(
212
            this SyntaxGenerator codeFactory,
P
Pilchie 已提交
213
            IPropertySymbol overriddenProperty,
214
            DeclarationModifiers modifiers,
P
Pilchie 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
            INamedTypeSymbol containingType,
            Document document,
            CancellationToken cancellationToken)
        {
            var getAccessibility = overriddenProperty.GetMethod.ComputeResultantAccessibility(containingType);
            var setAccessibility = overriddenProperty.SetMethod.ComputeResultantAccessibility(containingType);

            SyntaxNode getBody = null;
            SyntaxNode setBody = null;

            // Implement an abstract property by throwing not implemented in accessors.
            if (overriddenProperty.IsAbstract)
            {
                getBody = codeFactory.CreateThrowNotImplementStatement(document.Project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken));
                setBody = getBody;
            }
            else if (overriddenProperty.IsIndexer() && document.Project.Language == LanguageNames.CSharp)
            {
                // Indexer: return or set base[]. Only in C#, since VB must refer to these by name.
234 235 236
                getBody = codeFactory.ReturnStatement(
                    codeFactory.ElementAccessExpression(
                        codeFactory.BaseExpression(),
P
Pilchie 已提交
237 238
                        codeFactory.CreateArguments(overriddenProperty.Parameters)));

239 240 241 242
                setBody = codeFactory.ExpressionStatement(
                    codeFactory.AssignmentStatement(
                    codeFactory.ElementAccessExpression(
                        codeFactory.BaseExpression(),
P
Pilchie 已提交
243
                        codeFactory.CreateArguments(overriddenProperty.Parameters)),
244
                    codeFactory.IdentifierName("value")));
P
Pilchie 已提交
245 246 247 248 249
            }
            else if (overriddenProperty.GetParameters().Any())
            {
                // Call accessors directly if C# overriding VB
                if (document.Project.Language == LanguageNames.CSharp
250
                    && SymbolFinder.FindSourceDefinitionAsync(overriddenProperty, document.Project.Solution, cancellationToken)
P
Pilchie 已提交
251 252 253 254 255 256 257
                                    .WaitAndGetResult(CancellationToken.None).Language == LanguageNames.VisualBasic)
                {
                    var getName = overriddenProperty.GetMethod != null ? overriddenProperty.GetMethod.Name : null;
                    var setName = overriddenProperty.SetMethod != null ? overriddenProperty.SetMethod.Name : null;

                    getBody = getName == null
                        ? null
258 259 260 261 262
                        : codeFactory.ReturnStatement(
                    codeFactory.InvocationExpression(
                        codeFactory.MemberAccessExpression(
                            codeFactory.BaseExpression(),
                            codeFactory.IdentifierName(getName)),
P
Pilchie 已提交
263 264 265 266
                        codeFactory.CreateArguments(overriddenProperty.Parameters)));

                    setBody = setName == null
                        ? null
267 268 269 270 271
                        : codeFactory.ExpressionStatement(
                        codeFactory.InvocationExpression(
                            codeFactory.MemberAccessExpression(
                                codeFactory.BaseExpression(),
                                codeFactory.IdentifierName(setName)),
P
Pilchie 已提交
272 273 274 275
                            codeFactory.CreateArguments(overriddenProperty.SetMethod.GetParameters())));
                }
                else
                {
276 277 278 279 280 281 282 283 284 285 286 287
                    getBody = codeFactory.ReturnStatement(
                        codeFactory.InvocationExpression(
                        codeFactory.MemberAccessExpression(
                            codeFactory.BaseExpression(),
                            codeFactory.IdentifierName(overriddenProperty.Name)), codeFactory.CreateArguments(overriddenProperty.Parameters)));
                    setBody = codeFactory.ExpressionStatement(
                        codeFactory.AssignmentStatement(
                            codeFactory.InvocationExpression(
                            codeFactory.MemberAccessExpression(
                            codeFactory.BaseExpression(),
                        codeFactory.IdentifierName(overriddenProperty.Name)), codeFactory.CreateArguments(overriddenProperty.Parameters)),
                        codeFactory.IdentifierName("value")));
P
Pilchie 已提交
288 289 290 291 292
                }
            }
            else
            {
                // Regular property: return or set the base property
293 294 295 296 297 298 299 300 301 302
                getBody = codeFactory.ReturnStatement(
                    codeFactory.MemberAccessExpression(
                        codeFactory.BaseExpression(),
                        codeFactory.IdentifierName(overriddenProperty.Name)));
                setBody = codeFactory.ExpressionStatement(
                    codeFactory.AssignmentStatement(
                        codeFactory.MemberAccessExpression(
                        codeFactory.BaseExpression(),
                    codeFactory.IdentifierName(overriddenProperty.Name)),
                    codeFactory.IdentifierName("value")));
P
Pilchie 已提交
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
            }

            // Only generate a getter if the base getter is accessible.
            IMethodSymbol accessorGet = null;
            if (overriddenProperty.GetMethod != null && overriddenProperty.GetMethod.IsAccessibleWithin(containingType))
            {
                accessorGet = CodeGenerationSymbolFactory.CreateMethodSymbol(
                    overriddenProperty.GetMethod,
                    accessibility: getAccessibility,
                    statements: new[] { getBody },
                    modifiers: modifiers);
            }

            // Only generate a setter if the base setter is accessible.
            IMethodSymbol accessorSet = null;
            if (overriddenProperty.SetMethod != null &&
                overriddenProperty.SetMethod.IsAccessibleWithin(containingType) &&
                overriddenProperty.SetMethod.DeclaredAccessibility != Accessibility.Private)
            {
                accessorSet = CodeGenerationSymbolFactory.CreateMethodSymbol(
                    overriddenProperty.SetMethod,
                    accessibility: setAccessibility,
                    statements: new[] { setBody },
                    modifiers: modifiers);
            }

            return CodeGenerationSymbolFactory.CreatePropertySymbol(
                overriddenProperty,
                accessibility: overriddenProperty.ComputeResultantAccessibility(containingType),
                modifiers: modifiers,
                name: overriddenProperty.Name,
                isIndexer: overriddenProperty.IsIndexer(),
                getMethod: accessorGet,
                setMethod: accessorSet);
        }

        public static IEventSymbol OverrideEvent(
340
            this SyntaxGenerator codeFactory,
P
Pilchie 已提交
341
            IEventSymbol overriddenEvent,
342
            DeclarationModifiers modifiers,
P
Pilchie 已提交
343 344 345 346 347 348 349 350 351 352 353 354
            INamedTypeSymbol newContainingType)
        {
            return CodeGenerationSymbolFactory.CreateEventSymbol(
                overriddenEvent,
                attributes: null,
                accessibility: overriddenEvent.ComputeResultantAccessibility(newContainingType),
                modifiers: modifiers,
                explicitInterfaceSymbol: null,
                name: overriddenEvent.Name);
        }

        public static IMethodSymbol OverrideMethod(
355
            this SyntaxGenerator codeFactory,
P
Pilchie 已提交
356
            IMethodSymbol overriddenMethod,
357
            DeclarationModifiers modifiers,
P
Pilchie 已提交
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
            INamedTypeSymbol newContainingType,
            Document newDocument,
            CancellationToken cancellationToken)
        {
            // Abstract: Throw not implemented
            if (overriddenMethod.IsAbstract)
            {
                return CodeGenerationSymbolFactory.CreateMethodSymbol(
                    overriddenMethod,
                    accessibility: overriddenMethod.ComputeResultantAccessibility(newContainingType),
                    modifiers: modifiers,
                    statements: new[] { codeFactory.CreateThrowNotImplementStatement(newDocument.Project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken)) });
            }
            else
            {
                // Otherwise, call the base method with the same parameters
                var typeParams = overriddenMethod.GetTypeArguments();
375 376
                var body = codeFactory.InvocationExpression(
                    codeFactory.MemberAccessExpression(codeFactory.BaseExpression(),
P
Pilchie 已提交
377
                    typeParams.IsDefaultOrEmpty
378 379
                        ? codeFactory.IdentifierName(overriddenMethod.Name)
                        : codeFactory.GenericName(overriddenMethod.Name, typeParams)),
P
Pilchie 已提交
380 381 382 383 384 385 386
                    codeFactory.CreateArguments(overriddenMethod.GetParameters()));

                return CodeGenerationSymbolFactory.CreateMethodSymbol(
                    method: overriddenMethod,
                    accessibility: overriddenMethod.ComputeResultantAccessibility(newContainingType),
                    modifiers: modifiers,
                    statements: ((IMethodSymbol)overriddenMethod).ReturnsVoid
387 388
                        ? new SyntaxNode[] { codeFactory.ExpressionStatement(body) }
                        : new SyntaxNode[] { codeFactory.ReturnStatement(body) });
P
Pilchie 已提交
389 390 391
            }
        }
    }
392
}