From b001cfaae2f411793e78eb845eded094406bf2c7 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Thu, 25 Jun 2020 17:40:08 -0700 Subject: [PATCH] Extract SourcePropertySymbolBase class (#45411) Records need to provide a new form of source property symbol with different syntax. This PR extracts a new base class to try to share as much code as possible with the existing SourcePropertySymbol and the SynthesizedRecordPropertySymbol. --- .../Portable/Binder/Binder_Statements.cs | 104 +- .../CSharp/Portable/Binder/Binder_Symbols.cs | 73 +- .../Compiler/MethodBodySynthesizer.cs | 30 +- .../Portable/Compiler/MethodCompiler.cs | 42 +- .../Compiler/SynthesizedMetadataCompiler.cs | 8 +- .../Emitter/Model/PropertySymbolAdapter.cs | 4 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 14 +- .../FlowAnalysis/DefiniteAssignment.cs | 14 +- .../Portable/FlowAnalysis/NullableWalker.cs | 4 +- .../LocalRewriter_AssignmentOperator.cs | 2 +- .../SymbolDisplayVisitor.Members.cs | 14 +- .../Source/SourceMemberContainerSymbol.cs | 41 +- ...berContainerSymbol_ImplementationChecks.cs | 40 +- .../Source/SourceOrRecordPropertySymbol.cs | 38 - .../Source/SourcePropertyAccessorSymbol.cs | 100 +- .../Symbols/Source/SourcePropertySymbol.cs | 1718 ++--------------- .../Source/SourcePropertySymbolBase.cs | 1470 ++++++++++++++ .../SynthesizedRecordPropertySymbol.cs | 400 +--- ...SynthesizedAccessorValueParameterSymbol.cs | 4 +- .../SynthesizedBackingFieldSymbol.cs | 4 +- .../Test/Semantic/Semantics/RecordTests.cs | 17 + .../Test/Symbol/Symbols/Source/RecordTests.cs | 12 +- 22 files changed, 2089 insertions(+), 2064 deletions(-) delete mode 100644 src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs create mode 100644 src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index ea2cf793cc1..f9130d27830 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp internal partial class Binder { /// - /// This is the set of parameters and local variables that were used as arguments to + /// This is the set of parameters and local variables that were used as arguments to /// lock or using statements in enclosing scopes. /// /// @@ -153,8 +153,8 @@ public virtual BoundStatement BindStatement(StatementSyntax node, DiagnosticBag Debug.Assert(result.Syntax is StatementSyntax, "BoundStatement should be associated with a statement syntax."); - Debug.Assert(System.Linq.Enumerable.Contains(result.Syntax.AncestorsAndSelf(), node), @"Bound statement (or one of its parents) - should have same syntax as the given syntax node. + Debug.Assert(System.Linq.Enumerable.Contains(result.Syntax.AncestorsAndSelf(), node), @"Bound statement (or one of its parents) + should have same syntax as the given syntax node. Otherwise it may be confusing to the binder cache that uses syntax node as keys."); return result; @@ -467,7 +467,7 @@ private BoundLabeledStatement BindLabeled(LabeledStatementSyntax node, Diagnosti HashSet useSiteDiagnostics = null; var binder = this.LookupSymbolsWithFallback(result, node.Identifier.ValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, options: LookupOptions.LabelsOnly); - // result.Symbols can be empty in some malformed code, e.g. when a labeled statement is used an embedded statement in an if or foreach statement + // result.Symbols can be empty in some malformed code, e.g. when a labeled statement is used an embedded statement in an if or foreach statement // In this case we create new label symbol on the fly, and an error is reported by parser var symbol = result.Symbols.Count > 0 && result.IsMultiViable ? (LabelSymbol)result.Symbols.First() : @@ -776,7 +776,7 @@ private TypeWithAnnotations BindVariableTypeWithAnnotations(CSharpSyntaxNode dec if (isVar) { - // There are a number of ways in which a var decl can be illegal, but in these + // There are a number of ways in which a var decl can be illegal, but in these // cases we should report an error and then keep right on going with the inference. if (isConst) @@ -793,11 +793,11 @@ private TypeWithAnnotations BindVariableTypeWithAnnotations(CSharpSyntaxNode dec // users showed that there was no consensus on whether the above should mean // "double x = 10, y = 123.4;", taking the best type available and substituting // that for "var", or treating it as "var x = 10; var y = 123.4;" -- since there - // was no consensus we decided to simply make it illegal. + // was no consensus we decided to simply make it illegal. // // In dev10 for error recovery in the IDE we do an odd thing -- we simply take // the type of the first variable and use it. So that is "int x = 10, y = 123.4;". - // + // // This seems less than ideal. In the error recovery scenario it probably makes // more sense to treat that as "var x = 10; var y = 123.4;" and do each inference // separately. @@ -813,7 +813,7 @@ private TypeWithAnnotations BindVariableTypeWithAnnotations(CSharpSyntaxNode dec // In the native compiler when given a situation like // // D[] x; - // + // // where D is a static type we report both that D cannot be an element type // of an array, and that D[] is not a valid type for a local variable. // This seems silly; the first error is entirely sufficient. We no longer @@ -867,7 +867,7 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost BoundExpression expression = BindToNaturalType(BindValue(initializer, diagnostics, valueKind), diagnostics); - // Certain expressions (null literals, method groups and anonymous functions) have no type of + // Certain expressions (null literals, method groups and anonymous functions) have no type of // their own and therefore cannot be the initializer of an implicitly typed local. if (!expression.HasAnyErrors && !expression.HasExpressionType()) { @@ -990,7 +990,7 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost initializerOpt = BindInferredVariableInitializer(diagnostics, value, valueKind, localSymbol.RefKind, declarator); - // If we got a good result then swap the inferred type for the "var" + // If we got a good result then swap the inferred type for the "var" TypeSymbol initializerType = initializerOpt?.Type; if ((object)initializerType != null) { @@ -1030,7 +1030,7 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost initializerOpt = BindPossibleArrayInitializer(value, declTypeOpt.Type, valueKind, diagnostics); if (kind != LocalDeclarationKind.FixedVariable) { - // If this is for a fixed statement, we'll do our own conversion since there are some special cases. + // If this is for a fixed statement, we'll do our own conversion since there are some special cases. initializerOpt = GenerateConversionForAssignment( declTypeOpt.Type, initializerOpt, @@ -1091,9 +1091,9 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost if (kind == LocalDeclarationKind.FixedVariable || kind == LocalDeclarationKind.UsingVariable) { - // CONSIDER: The error message is "you must provide an initializer in a fixed - // CONSIDER: or using declaration". The error message could be targeted to - // CONSIDER: the actual situation. "you must provide an initializer in a + // CONSIDER: The error message is "you must provide an initializer in a fixed + // CONSIDER: or using declaration". The error message could be targeted to + // CONSIDER: the actual situation. "you must provide an initializer in a // CONSIDER: 'fixed' declaration." if (initializerOpt == null) @@ -1167,8 +1167,8 @@ protected bool CheckRefLocalInAsyncOrIteratorMethod(SyntaxToken identifierToken, internal ImmutableArray BindDeclaratorArguments(VariableDeclaratorSyntax declarator, DiagnosticBag diagnostics) { - // It is possible that we have a bracketed argument list, like "int x[];" or "int x[123];" - // in a non-fixed-size-array declaration . This is a common error made by C++ programmers. + // It is possible that we have a bracketed argument list, like "int x[];" or "int x[123];" + // in a non-fixed-size-array declaration . This is a common error made by C++ programmers. // We have already given a good error at parse time telling the user to either make it "fixed" // or to move the brackets to the type. However, we should still do semantic analysis of // the arguments, so that errors in them are discovered, hovering over them in the IDE @@ -1279,7 +1279,7 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym break; } - // if the feature was enabled, but something went wrong with the method, report that, otherwise don't. + // if the feature was enabled, but something went wrong with the method, report that, otherwise don't. // If feature is not enabled, additional errors would be just noise. bool extensibleFixedEnabled = ((CSharpParseOptions)initializerOpt.SyntaxTree.Options)?.IsFeatureEnabled(MessageID.IDS_FeatureExtensibleFixedStatement) != false; if (extensibleFixedEnabled) @@ -1466,11 +1466,11 @@ private BoundExpression InferTypeForDiscardAssignment(BoundDiscardExpression op1 if (!op1.HasAnyErrors) { - // Build bound conversion. The node might not be used if this is a dynamic conversion + // Build bound conversion. The node might not be used if this is a dynamic conversion // but diagnostics should be reported anyways. var conversion = GenerateConversionForAssignment(op1.Type, op2, diagnostics, isRefAssignment: isRef); - // If the result is a dynamic assignment operation (SetMember or SetIndex), + // If the result is a dynamic assignment operation (SetMember or SetIndex), // don't generate the boxing conversion to the dynamic type. // Leave the values as they are, and deal with the conversions at runtime. if (op1.Kind != BoundKind.DynamicIndexerAccess && @@ -1621,7 +1621,7 @@ private static bool AccessingAutoPropertyFromConstructor(BoundExpression receive propertySymbol = propertySymbol.OriginalDefinition; } - var sourceProperty = propertySymbol as SourceOrRecordPropertySymbol; + var sourceProperty = propertySymbol as SourcePropertySymbolBase; var propertyIsStatic = propertySymbol.IsStatic; return (object)sourceProperty != null && @@ -1766,13 +1766,13 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, Debug.Assert((object)targetType != null); Debug.Assert(expression != null); - // We wish to avoid "cascading" errors, so if the expression we are + // We wish to avoid "cascading" errors, so if the expression we are // attempting to convert to a type had errors, suppress additional - // diagnostics. However, if the expression + // diagnostics. However, if the expression // with errors is an unbound lambda then the errors are almost certainly // syntax errors. For error recovery analysis purposes we wish to bind // error lambdas like "Action f = x=>{ x. };" because IntelliSense - // needs to know that x is of type int. + // needs to know that x is of type int. if (expression.HasAnyErrors && expression.Kind != BoundKind.UnboundLambda) { @@ -1796,7 +1796,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, } else if (!conversion.IsImplicit || !conversion.IsValid) { - // We suppress conversion errors on default parameters; eg, + // We suppress conversion errors on default parameters; eg, // if someone says "void M(string s = 123) {}". We will report // a special error in the default parameter binder. @@ -1832,7 +1832,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, var reason = Conversions.IsAnonymousFunctionCompatibleWithType(anonymousFunction, targetType); - // It is possible that the conversion from lambda to delegate is just fine, and + // It is possible that the conversion from lambda to delegate is just fine, and // that we ended up here because the target type, though itself is not an error // type, contains a type argument which is an error type. For example, converting // (Goo goo)=>{} to Action is a perfectly legal conversion even if Goo is undefined! @@ -1875,7 +1875,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, var delegateType = targetType.GetDelegateType(); - // The target type is a valid delegate or expression tree type. Is there something wrong with the + // The target type is a valid delegate or expression tree type. Is there something wrong with the // parameter list? // First off, is there a parameter list at all? @@ -1889,7 +1889,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, // D d = delegate {}; // // error CS1676: Parameter 1 must be declared with the 'out' keyword - // error CS1688: Cannot convert anonymous method block without a parameter list + // error CS1688: Cannot convert anonymous method block without a parameter list // to delegate type 'D' because it has one or more out parameters // // This seems redundant, (because there is no "parameter 1" in the source code) @@ -1910,7 +1910,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, // The parameter list exists and had the right number of parameters. Were any of its types bad? - // If any parameter type of the lambda is an error type then suppress + // If any parameter type of the lambda is an error type then suppress // further errors. We've already reported errors on the bad type. if (anonymousFunction.HasExplicitlyTypedParameterList) { @@ -2041,7 +2041,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, // CLEVERNESS: By passing ConstantValue.Bad, we tell HasImplicitConstantExpressionConversion to ignore the constant // value and only consider the types. - // If there would be an implicit constant conversion for a different constant of the same type + // If there would be an implicit constant conversion for a different constant of the same type // (i.e. one that's not out of range), then it's more helpful to report the range check failure // than to suggest inserting a cast. Error(diagnostics, ErrorCode.ERR_ConstOutOfRange, syntax, sourceConstantValueOpt.Value, targetType); @@ -2266,9 +2266,9 @@ void reportMethodGroupErrors(BoundMethodGroup methodGroup, bool fromAddressOf) // report all leaf elements of the tuple literal that failed to convert // NOTE: we are not responsible for reporting use site errors here, just the failed leaf conversions. - // By the time we get here we have done analysis and know we have failed the cast in general, and diagnostics collected in the process is already in the bag. + // By the time we get here we have done analysis and know we have failed the cast in general, and diagnostics collected in the process is already in the bag. // The only thing left is to form a diagnostics about the actually failing conversion(s). - // This whole method does not itself collect any usesite diagnostics. Its only purpose is to produce an error better than "conversion failed here" + // This whole method does not itself collect any usesite diagnostics. Its only purpose is to produce an error better than "conversion failed here" HashSet usDiagsUnused = null; for (int i = 0; i < targetElementTypes.Length; i++) @@ -2296,24 +2296,24 @@ private BoundStatement BindIfStatement(IfStatementSyntax node, DiagnosticBag dia internal BoundExpression BindBooleanExpression(ExpressionSyntax node, DiagnosticBag diagnostics) { - // SPEC: - // A boolean-expression is an expression that yields a result of type bool; - // either directly or through application of operator true in certain + // SPEC: + // A boolean-expression is an expression that yields a result of type bool; + // either directly or through application of operator true in certain // contexts as specified in the following. // - // The controlling conditional expression of an if-statement, while-statement, - // do-statement, or for-statement is a boolean-expression. The controlling - // conditional expression of the ?: operator follows the same rules as a + // The controlling conditional expression of an if-statement, while-statement, + // do-statement, or for-statement is a boolean-expression. The controlling + // conditional expression of the ?: operator follows the same rules as a // boolean-expression, but for reasons of operator precedence is classified // as a conditional-or-expression. // - // A boolean-expression is required to be implicitly convertible to bool - // or of a type that implements operator true. If neither requirement + // A boolean-expression is required to be implicitly convertible to bool + // or of a type that implements operator true. If neither requirement // is satisfied, a binding-time error occurs. // - // When a boolean expression cannot be implicitly converted to bool but does - // implement operator true, then following evaluation of the expression, - // the operator true implementation provided by that type is invoked + // When a boolean expression cannot be implicitly converted to bool but does + // implement operator true, then following evaluation of the expression, + // the operator true implementation provided by that type is invoked // to produce a bool value. // // SPEC ERROR: The third paragraph above is obviously not correct; we need @@ -2420,7 +2420,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, Diagnostic diagnostics: diagnostics); // Consider op_true to be compiler-generated so that it doesn't appear in the semantic model. - // UNDONE: If we decide to expose the operator in the semantic model, we'll have to remove the + // UNDONE: If we decide to expose the operator in the semantic model, we'll have to remove the // WasCompilerGenerated flag (and possibly suppress the symbol in specific APIs). return new BoundUnaryOperator(node, signature.Kind, resultOperand, ConstantValue.NotAvailable, signature.Method, resultKind, originalUserDefinedOperators, signature.ReturnType) { @@ -2511,7 +2511,7 @@ internal BoundStatement BindForOrUsingOrFixedDeclarations(VariableDeclarationSyn if (isVar && count > 1) { - // There are a number of ways in which a var decl can be illegal, but in these + // There are a number of ways in which a var decl can be illegal, but in these // cases we should report an error and then keep right on going with the inference. Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableMultipleDeclarator, nodeOpt); @@ -2729,8 +2729,8 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di return new BoundReturnStatement(syntax, refKind, BindToTypeForErrorRecovery(arg), hasErrors: true); } - // The return type could be null; we might be attempting to infer the return type either - // because of method type inference, or because we are attempting to do error analysis + // The return type could be null; we might be attempting to infer the return type either + // because of method type inference, or because we are attempting to do error analysis // on a lambda expression of unknown return type. if ((object)retType != null) { @@ -2742,7 +2742,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di var lambda = container as LambdaSymbol; if ((object)lambda != null) { - // Error case: void-returning or async task-returning method or lambda with "return x;" + // Error case: void-returning or async task-returning method or lambda with "return x;" var errorCode = retType.IsVoidType() ? ErrorCode.ERR_RetNoObjectRequiredLambda : ErrorCode.ERR_TaskRetNoObjectRequiredLambda; @@ -2754,12 +2754,12 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di // COMPATIBILITY: The native compiler also produced an error // COMPATIBILITY: "Cannot convert lambda expression to delegate type 'Action' because some of the // COMPATIBILITY: return types in the block are not implicitly convertible to the delegate return type" - // COMPATIBILITY: This error doesn't make sense in the "void" case because the whole idea of + // COMPATIBILITY: This error doesn't make sense in the "void" case because the whole idea of // COMPATIBILITY: "conversion to void" is a bit unusual, and we've already given a good error. } else { - // Error case: void-returning or async task-returning method or lambda with "return x;" + // Error case: void-returning or async task-returning method or lambda with "return x;" var errorCode = retType.IsVoidType() ? ErrorCode.ERR_RetNoObjectRequired : ErrorCode.ERR_TaskRetNoObjectRequired; @@ -2963,7 +2963,7 @@ private BoundCatchBlock BindCatchBlock(CatchClauseSyntax node, ArrayBuilder - /// Wrap a given expression e into a block as either { e; } or { return e; } + /// Wrap a given expression e into a block as either { e; } or { return e; } /// Shared between lambda and expression-bodied method binding. /// internal BoundBlock CreateBlockFromExpression(CSharpSyntaxNode node, ImmutableArray locals, RefKind refKind, BoundExpression expression, ExpressionSyntax expressionSyntax, DiagnosticBag diagnostics) @@ -3422,7 +3422,7 @@ internal virtual ImmutableArray Labels } /// - /// Perform a lookup for the specified method on the specified expression by attempting to invoke it + /// Perform a lookup for the specified method on the specified expression by attempting to invoke it /// /// The expression to perform pattern lookup on /// Method to search for. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 31f4f4729a6..b7c177257d9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -383,17 +383,17 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeSymbol(E #nullable enable /// - /// Bind the syntax into a namespace, type or alias symbol. + /// Bind the syntax into a namespace, type or alias symbol. /// /// /// This method is used in deeply recursive parts of the compiler. Specifically this and /// - /// are mutually recursive. The non-recursive parts of this method tend to reserve significantly large + /// are mutually recursive. The non-recursive parts of this method tend to reserve significantly large /// stack frames due to their use of large struct like . /// - /// To keep the stack frame size on recursive paths small the non-recursive parts are factored into local - /// functions. This means we pay their stack penalty only when they are used. They are themselves big - /// enough they should be disqualified from inlining. In the future when attributes are allowed on + /// To keep the stack frame size on recursive paths small the non-recursive parts are factored into local + /// functions. This means we pay their stack penalty only when they are used. They are themselves big + /// enough they should be disqualified from inlining. In the future when attributes are allowed on /// local functions we should explicitly mark them as /// internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasSymbol(ExpressionSyntax syntax, DiagnosticBag diagnostics, ConsList basesBeingResolved, bool suppressUseSiteDiagnostics) @@ -757,7 +757,7 @@ private NamedTypeSymbol BindPredefinedTypeSymbol(PredefinedTypeSyntax node, Diag } /// - /// Binds a simple name or the simple name portion of a qualified name. + /// Binds a simple name or the simple name portion of a qualified name. /// private NamespaceOrTypeOrAliasSymbolWithAnnotations BindSimpleNamespaceOrTypeOrAliasSymbol( SimpleNameSyntax syntax, @@ -766,10 +766,10 @@ private NamedTypeSymbol BindPredefinedTypeSymbol(PredefinedTypeSyntax node, Diag bool suppressUseSiteDiagnostics, NamespaceOrTypeSymbol qualifierOpt = null) { - // Note that the comment above is a small lie; there is no such thing as the "simple name portion" of + // Note that the comment above is a small lie; there is no such thing as the "simple name portion" of // a qualified alias member expression. A qualified alias member expression has the form // "identifier :: identifier optional-type-arguments" -- the right hand side of which - // happens to match the syntactic form of a simple name. As a convenience, we analyze the + // happens to match the syntactic form of a simple name. As a convenience, we analyze the // right hand side of the "::" here because it is so similar to a simple name; the left hand // side is in qualifierOpt. @@ -819,7 +819,7 @@ private static bool IsViableType(LookupResult result) var identifierValueText = node.Identifier.ValueText; // If we are here in an error-recovery scenario, say, "goo(123);" then - // we might have an 'empty' simple name. In that case do not report an + // we might have an 'empty' simple name. In that case do not report an // 'unable to find ""' error; we've already reported an error in the parser so // just bail out with an error symbol. @@ -945,9 +945,9 @@ private static LookupOptions GetSimpleNameLookupOptions(NameSyntax node, bool is { if (SyntaxFacts.IsAttributeName(node)) { - // SPEC: By convention, attribute classes are named with a suffix of Attribute. + // SPEC: By convention, attribute classes are named with a suffix of Attribute. // SPEC: An attribute-name of the form type-name may either include or omit this suffix. - // SPEC: If an attribute class is found both with and without this suffix, an ambiguity + // SPEC: If an attribute class is found both with and without this suffix, an ambiguity // SPEC: is present, and a compile-time error results. If the attribute-name is spelled // SPEC: such that its right-most identifier is a verbatim identifier (ยง2.4.2), then only // SPEC: an attribute without a suffix is matched, thus enabling such an ambiguity to be resolved. @@ -1036,10 +1036,10 @@ private Symbol UnwrapAlias(Symbol symbol, out AliasSymbol alias, DiagnosticBag d // us an identifier followed by a type argument list. Therefore they // must expect the result to be a generic type, and not a namespace or alias. - // The result of this method will therefore always be a type symbol of the + // The result of this method will therefore always be a type symbol of the // correct arity, though it might have to be an error type. - // We might be asked to bind a generic simple name of the form "T<,,,>", + // We might be asked to bind a generic simple name of the form "T<,,,>", // which is only legal in the context of "typeof(T<,,,>)". If we are given // no type arguments and we are not in such a context, we'll give an error. @@ -1051,15 +1051,15 @@ private Symbol UnwrapAlias(Symbol symbol, out AliasSymbol alias, DiagnosticBag d // * Lookup could fail to find anything at all. // * Lookup could find a type of the wrong arity // * Lookup could find something but it is not a type. - // + // // Second, we could be asked to resolve an unbound type T<,,,> when - // not in a context where it is legal to do so. Note that this is - // intended an improvement over the analysis performed by the - // native compiler; in the native compiler we catch bad uses of unbound + // not in a context where it is legal to do so. Note that this is + // intended an improvement over the analysis performed by the + // native compiler; in the native compiler we catch bad uses of unbound // types at parse time, not at semantic analysis time. That means that // we end up giving confusing "unexpected comma" or "expected type" // errors when it would be more informative to the user to simply - // tell them that an unbound type is not legal in this position. + // tell them that an unbound type is not legal in this position. // // This also means that we can get semantic analysis of the open // type in the IDE even in what would have been a syntax error case @@ -1072,11 +1072,11 @@ private Symbol UnwrapAlias(Symbol symbol, out AliasSymbol alias, DiagnosticBag d // a partially unbound type. // // The heuristic we will use is that the former kind of error takes priority - // over the latter; if the meaning of "Bogus<>" cannot be successfully + // over the latter; if the meaning of "Bogus<>" cannot be successfully // determined then there is no point telling the user that in addition, // it is syntactically wrong. Moreover, at this point we do not know what they - // mean by the remainder ".Blah" of the expression and so it seems wrong to - // deduce more errors from it. + // mean by the remainder ".Blah" of the expression and so it seems wrong to + // deduce more errors from it. var plainName = node.Identifier.ValueText; @@ -1114,9 +1114,9 @@ private Symbol UnwrapAlias(Symbol symbol, out AliasSymbol alias, DiagnosticBag d } else { - // It's not an unbound type expression, so we must have type arguments, and we have a - // generic type of the correct arity in hand (possibly an error type). Bind the type - // arguments and construct the final result. + // It's not an unbound type expression, so we must have type arguments, and we have a + // generic type of the correct arity in hand (possibly an error type). Bind the type + // arguments and construct the final result. resultType = ConstructNamedType( unconstructedType, node, @@ -1172,12 +1172,12 @@ private Symbol UnwrapAlias(Symbol symbol, out AliasSymbol alias, DiagnosticBag d // In the third case we will be given back the symbol -- say, a local variable symbol. // // In all three cases the appropriate error has already been reported. (That the - // type was not found, that the generic type found does not have that arity, that + // type was not found, that the generic type found does not have that arity, that // the non-generic type found cannot be used with a type argument list, or that // the symbol found is not something that takes type arguments. ) // The first thing to do is to make sure that we have some sort of generic type in hand. - // (Note that an error type symbol is always a generic type.) + // (Note that an error type symbol is always a generic type.) NamedTypeSymbol type = lookupResultSymbol as NamedTypeSymbol; @@ -1489,7 +1489,16 @@ internal static bool ReportUseSiteDiagnostics(Symbol symbol, DiagnosticBag diagn /// internal NamedTypeSymbol GetWellKnownType(WellKnownType type, DiagnosticBag diagnostics, SyntaxNode node) { - return GetWellKnownType(this.Compilation, type, diagnostics, node.Location); + return GetWellKnownType(type, diagnostics, node.Location); + } + + /// + /// This is a layer on top of the Compilation version that generates a diagnostic if the well-known + /// type isn't found. + /// + internal NamedTypeSymbol GetWellKnownType(WellKnownType type, DiagnosticBag diagnostics, Location location) + { + return GetWellKnownType(this.Compilation, type, diagnostics, location); } /// @@ -1892,7 +1901,7 @@ public int Compare(Symbol fst, Symbol snd) Compilation.IsAttributeType((NamedTypeSymbol)first) && Compilation.IsAttributeType((NamedTypeSymbol)second)) { - // SPEC: If an attribute class is found both with and without Attribute suffix, an ambiguity + // SPEC: If an attribute class is found both with and without Attribute suffix, an ambiguity // SPEC: is present, and a compile-time error results. info = new CSDiagnosticInfo(ErrorCode.ERR_AmbiguousAttribute, originalSymbols, @@ -1960,7 +1969,7 @@ public int Compare(Symbol fst, Symbol snd) } else if (singleResult.Kind == SymbolKind.ErrorType) { - // We want to report ERR_CircularBase error on the spot to make sure + // We want to report ERR_CircularBase error on the spot to make sure // that the right location is used for it. var errorType = (ErrorTypeSymbol)singleResult; @@ -2341,7 +2350,7 @@ protected AssemblySymbol GetForwardedToAssembly(string fullName, DiagnosticBag d /// The name of the (potentially) forwarded type. /// The arity of the forwarded type. /// The namespace of the potentially forwarded type. If none is provided, will - /// try Usings of the current import for eligible namespaces and return the namespace of the found forwarder, + /// try Usings of the current import for eligible namespaces and return the namespace of the found forwarder, /// if any. /// Will be used to report non-fatal errors during look up. /// Location to report errors on. @@ -2355,8 +2364,8 @@ protected AssemblySymbol GetForwardedToAssembly(string name, int arity, ref Name // If we are in the process of binding assembly level attributes, we might get into an infinite cycle // if any of the referenced assemblies forwards type to this assembly. Since forwarded types // are specified through assembly level attributes, an attempt to resolve the forwarded type - // might require us to examine types forwarded by this assembly, thus binding assembly level - // attributes again. And the cycle continues. + // might require us to examine types forwarded by this assembly, thus binding assembly level + // attributes again. And the cycle continues. // So, we won't do the analysis in this case, at the expense of better diagnostics. if ((this.Flags & BinderFlags.InContextualAttributeBinder) != 0) { diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs index 60622484421..1fc5ee975f8 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp { /// - /// Contains methods related to synthesizing bound nodes in initial binding + /// Contains methods related to synthesizing bound nodes in initial binding /// form that needs lowering, primarily method bodies for compiler-generated methods. /// internal static class MethodBodySynthesizer @@ -25,8 +25,8 @@ internal static class MethodBodySynthesizer { // Script field initializers have to be emitted after the call to the base constructor because they can refer to "this" instance. // - // Unlike regular field initializers, initializers of global script variables can access "this" instance. - // If the base class had a constructor that initializes its state a global variable would access partially initialized object. + // Unlike regular field initializers, initializers of global script variables can access "this" instance. + // If the base class had a constructor that initializes its state a global variable would access partially initialized object. // For this reason Script class must always derive directly from a class that has no state (System.Object). SyntaxNode syntax = loweredBody.Syntax; @@ -172,7 +172,7 @@ internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMemberMethodS { Debug.Assert(accessor.MethodKind == MethodKind.PropertyGet || accessor.MethodKind == MethodKind.PropertySet); - var property = (SourcePropertySymbol)accessor.AssociatedSymbol; + var property = (SourcePropertySymbolBase)accessor.AssociatedSymbol; CSharpSyntaxNode syntax = property.CSharpSyntaxNode; BoundExpression thisReference = null; if (!accessor.IsStatic) @@ -219,10 +219,10 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody(SourceEventSymbol /// /// Generate a thread-safe accessor for a WinRT field-like event. - /// + /// /// Add: /// return EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddEventHandler(value); - /// + /// /// Remove: /// EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveEventHandler(value); /// @@ -302,7 +302,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEvent { // { // return EventRegistrationTokenTable.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value); - // } + // } BoundStatement returnStatement = BoundReturnStatement.Synthesized(syntax, RefKind.None, processHandlerCall); return BoundBlock.SynthesizedNoLocals(syntax, returnStatement); } @@ -311,7 +311,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEvent // { // EventRegistrationTokenTable.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveHandler(value); // return; - // } + // } BoundStatement callStatement = new BoundExpressionStatement(syntax, processHandlerCall); BoundStatement returnStatement = new BoundReturnStatement(syntax, RefKind.None, expressionOpt: null); return BoundBlock.SynthesizedNoLocals(syntax, callStatement, returnStatement); @@ -320,7 +320,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEvent /// /// Generate a thread-safe accessor for a regular field-like event. - /// + /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; @@ -329,12 +329,12 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEvent /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); - /// + /// /// Note, if System.Threading.Interlocked.CompareExchange<T> is not available, /// we emit the following code and mark the method Synchronized (unless it is a struct). - /// + /// /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -= - /// + /// /// internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { @@ -522,7 +522,7 @@ internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlo Debug.Assert(method.MethodKind == MethodKind.Destructor); Debug.Assert(syntax.Kind() == SyntaxKind.Block || syntax.Kind() == SyntaxKind.ArrowExpressionClause); - // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact + // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact // requirements), then we need to call that method in a finally block. Otherwise, just return block as-is. // NOTE: the Finalize method need not be a destructor or be overridden by the current method. MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method); @@ -572,11 +572,11 @@ internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlo } /// - /// Look for a base type method named "Finalize" that is protected (or protected internal), has no parameters, + /// Look for a base type method named "Finalize" that is protected (or protected internal), has no parameters, /// and returns void. It doesn't need to be virtual or a destructor. /// /// - /// You may assume that this would share code and logic with PEMethodSymbol.OverridesRuntimeFinalizer, + /// You may assume that this would share code and logic with PEMethodSymbol.OverridesRuntimeFinalizer, /// but FUNCBRECCS::bindDestructor has its own loop that performs these checks (differently). /// private static MethodSymbol GetBaseTypeFinalizeMethod(MethodSymbol method) diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 584e0b251b4..950919f3bfc 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -39,16 +39,16 @@ internal sealed class MethodCompiler : CSharpSymbolVisitor _compilerTasks; @@ -70,7 +70,7 @@ private void SetGlobalErrorIfTrue(bool arg) // It is ok if other tasks will see the change after some delay or does not observe at all. // Such races are unavoidable and will just result in performing some work that is safe to do // but may no longer be needed. - // The final Join of compiling tasks cannot happen without interlocked operations and that + // The final Join of compiling tasks cannot happen without interlocked operations and that // will ensure that any write of the flag is globally visible. if (arg) { @@ -119,7 +119,7 @@ private void SetGlobalErrorIfTrue(bool arg) if (compilation.PreviousSubmission != null) { - // In case there is a previous submission, we should ensure + // In case there is a previous submission, we should ensure // it has already created anonymous type/delegates templates // NOTE: if there are any errors, we will pick up what was created anyway @@ -515,7 +515,7 @@ private void CompileNamedType(NamedTypeSymbol containingType) case SymbolKind.Property: { - SourcePropertySymbol sourceProperty = member as SourcePropertySymbol; + var sourceProperty = member as SourcePropertySymbolBase; if ((object)sourceProperty != null && sourceProperty.IsSealed && compilationState.Emitting) { CompileSynthesizedSealedAccessors(sourceProperty, compilationState); @@ -690,7 +690,7 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) var importChain = methodWithBody.ImportChain; compilationState.CurrentImportChain = importChain; - // We make sure that an asynchronous mutation to the diagnostic bag does not + // We make sure that an asynchronous mutation to the diagnostic bag does not // confuse the method body generator by making a fresh bag and then loading // any diagnostics emitted into it back into the main diagnostic bag. var diagnosticsThisMethod = DiagnosticBag.GetInstance(); @@ -702,7 +702,7 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) _moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(method, method, diagnosticsThisMethod); // 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, + // 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; MethodBody emittedBody = null; @@ -775,7 +775,7 @@ private static bool IsFieldLikeEventAccessor(MethodSymbol method) } /// - /// In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a + /// In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a /// base type from another assembly) it is necessary for the compiler to generate explicit implementations for /// some interface methods. They don't go in the symbol table, but if we are emitting, then we should /// generate code for them. @@ -797,7 +797,7 @@ private void CompileSynthesizedExplicitImplementations(SourceMemberContainerType } } - private void CompileSynthesizedSealedAccessors(SourcePropertySymbol sourceProperty, TypeCompilationState compilationState) + private void CompileSynthesizedSealedAccessors(SourcePropertySymbolBase sourceProperty, TypeCompilationState compilationState) { SynthesizedSealedPropertyAccessor synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt; @@ -991,9 +991,9 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum UnassignedFieldsWalker.Analyze(_compilation, methodSymbol, body, diagsForCurrentMethod); } - // lower initializers just once. the lowered tree will be reused when emitting all constructors + // lower initializers just once. the lowered tree will be reused when emitting all constructors // with field initializers. Once lowered, these initializers will be stashed in processedInitializers.LoweredInitializers - // (see later in this method). Don't bother lowering _now_ if this particular ctor won't have the initializers + // (see later in this method). Don't bother lowering _now_ if this particular ctor won't have the initializers // appended to its body. if (includeInitializersInBody && processedInitializers.LoweredInitializers == null) { @@ -1098,7 +1098,7 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum _compilation.EventQueue.TryEnqueue(new SymbolDeclaredCompilationEvent(_compilation, methodSymbol.GetPublicSymbol(), lazySemanticModel)); } - // Don't lower if we're not emitting or if there were errors. + // Don't lower if we're not emitting or if there were errors. // Methods that had binding errors are considered too broken to be lowered reliably. if (_moduleBeingBuiltOpt == null || hasErrors) { @@ -1108,7 +1108,7 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum // ############################ // LOWERING AND EMIT - // Any errors generated below here are considered Emit diagnostics + // Any errors generated below here are considered Emit diagnostics // and will not be reported to callers Compilation.GetDiagnostics() ImmutableArray dynamicAnalysisSpans = ImmutableArray.Empty; @@ -1320,9 +1320,9 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum if (sawAwaitInExceptionHandler) { - // If we have awaits in handlers, we need to + // If we have awaits in handlers, we need to // replace handlers with synthetic ones which can be consumed by async rewriter. - // The reason why this rewrite happens before the lambda rewrite + // The reason why this rewrite happens before the lambda rewrite // is that we may need access to exception locals and it would be fairly hard to do // if these locals are captured into closures (possibly nested ones). loweredBody = AsyncExceptionHandlerRewriter.Rewrite( @@ -1442,7 +1442,7 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum isAsyncStateMachine = kickoffMethod.IsAsync; - // Async void method may be partial. Debug info needs to be associated with the emitted definition, + // Async void method may be partial. Debug info needs to be associated with the emitted definition, // but the kickoff method is the method implementation (the part with body). kickoffMethod = kickoffMethod.PartialDefinitionPart ?? kickoffMethod; } @@ -1488,7 +1488,7 @@ public override object VisitField(FieldSymbol symbol, TypeCompilationState argum var stateMachineHoistedLocalScopes = ((object)kickoffMethod != null) ? builder.GetHoistedLocalScopes() : default(ImmutableArray); - // Translate the imports even if we are not writing PDBs. The translation has an impact on generated metadata + // Translate the imports even if we are not writing PDBs. The translation has an impact on generated metadata // and we don't want to emit different metadata depending on whether or we emit with PDB stream. // TODO (https://github.com/dotnet/roslyn/issues/2846): This will need to change for member initializers in partial class. var importScopeOpt = importChainOpt?.Translate(moduleBuilder, diagnosticsForThisMethod); @@ -1722,7 +1722,7 @@ internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSt } else { - var property = sourceMethod.AssociatedSymbol as SourcePropertySymbol; + var property = sourceMethod.AssociatedSymbol as SourcePropertySymbolBase; if ((object)property != null && property.IsAutoProperty) { return MethodBodySynthesizer.ConstructAutoPropertyAccessorBody(sourceMethod); @@ -1863,7 +1863,7 @@ private static void ReportCtorInitializerCycles(MethodSymbol method, BoundExpres // to be inside the body of a derived class in order for it to be in the // accessibility domain of the protected base class ctor. // - // In the second case the binder could be the binder associated with + // In the second case the binder could be the binder associated with // the body of D2; since the implicit call to base() will have no arguments // there is no need to look up "x". Binder outerBinder; @@ -1871,7 +1871,7 @@ private static void ReportCtorInitializerCycles(MethodSymbol method, BoundExpres if ((object)sourceConstructor == null) { // The constructor is implicit. We need to get the binder for the body - // of the enclosing class. + // of the enclosing class. CSharpSyntaxNode containerNode = constructor.GetNonNullSyntaxNode(); BinderFactory binderFactory = compilation.GetBinderFactory(containerNode.SyntaxTree); diff --git a/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs index bdbb19e68ee..b2747e63356 100644 --- a/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs @@ -14,9 +14,9 @@ namespace Microsoft.CodeAnalysis.CSharp { /// - /// When compiling in metadata-only mode, is not run. This is problematic because + /// When compiling in metadata-only mode, is not run. This is problematic because /// adds synthesized explicit implementations to the list of synthesized definitions. - /// In lieu of running , this class performs a quick + /// In lieu of running , this class performs a quick /// traversal of the symbol table and performs processing of synthesized symbols if necessary /// internal sealed class SynthesizedMetadataCompiler : CSharpSymbolVisitor @@ -66,7 +66,7 @@ public override void VisitNamedType(NamedTypeSymbol symbol) { if (_moduleBeingBuilt != null) { - // In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a + // In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a // base type from another assembly) it is necessary for the compiler to generate explicit implementations for // some interface methods. They don't go in the symbol table, but if we are emitting metadata, then we should // generate MethodDef entries for them. @@ -91,7 +91,7 @@ public override void VisitNamedType(NamedTypeSymbol symbol) public override void VisitProperty(PropertySymbol symbol) { - var sourceProperty = symbol as SourcePropertySymbol; + var sourceProperty = symbol as SourcePropertySymbolBase; if ((object)sourceProperty != null && sourceProperty.IsSealed) { var synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt; diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs index 9fd66b69636..515243c7303 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs @@ -33,7 +33,7 @@ IEnumerable IPropertyDefinition.GetAccessors(EmitContext conte yield return setMethod; } - SourcePropertySymbol sourceProperty = this as SourcePropertySymbol; + var sourceProperty = this as SourcePropertySymbolBase; if ((object)sourceProperty != null && sourceProperty.ShouldInclude(context)) { SynthesizedSealedPropertyAccessor synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt; @@ -266,7 +266,7 @@ string INamedEntity.Name private IMethodReference GetSynthesizedSealedAccessor(MethodKind targetMethodKind) { - SourcePropertySymbol sourceProperty = this as SourcePropertySymbol; + var sourceProperty = this as SourcePropertySymbolBase; if ((object)sourceProperty != null) { SynthesizedSealedPropertyAccessor synthesized = sourceProperty.SynthesizedSealedAccessorOpt; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 1d723640a7b..624de88c4eb 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -563,7 +563,7 @@ protected void VisitLvalue(BoundExpression node) if (Binder.AccessingAutoPropertyFromConstructor(access, _symbol)) { - var backingField = (access.PropertySymbol as SourcePropertySymbol)?.BackingField; + var backingField = (access.PropertySymbol as SourcePropertySymbolBase)?.BackingField; if (backingField != null) { VisitFieldAccessInternal(access.ReceiverOpt, backingField); @@ -1077,7 +1077,7 @@ public override BoundNode VisitStringInsert(BoundStringInsert node) public override BoundNode VisitArgList(BoundArgList node) { - // The "__arglist" expression that is legal inside a varargs method has no + // The "__arglist" expression that is legal inside a varargs method has no // effect on flow analysis and it has no children. return null; } @@ -1576,7 +1576,7 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) RestorePending(pendingBeforeTry); // NOTE: At this point all branches that are internal to try or catch blocks have been resolved. - // However we have not yet restored the oldPending branches. Therefore all the branches + // However we have not yet restored the oldPending branches. Therefore all the branches // that are currently pending must have been introduced in try/catch and do not terminate inside those blocks. // // With exception of YieldReturn, these branches logically go through finally, if such present, @@ -1811,7 +1811,7 @@ protected virtual void PropertySetter(BoundExpression node, BoundExpression rece VisitReceiverAfterCall(receiver, setter); } - // returns false if expression is not a property access + // returns false if expression is not a property access // or if the property has a backing field // and accessed in a corresponding constructor private bool RegularPropertyAccess(BoundExpression expr) @@ -1958,7 +1958,7 @@ public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) if (Binder.AccessingAutoPropertyFromConstructor(node, _symbol)) { - var backingField = (property as SourcePropertySymbol)?.BackingField; + var backingField = (property as SourcePropertySymbolBase)?.BackingField; if (backingField != null) { VisitFieldAccessInternal(node.ReceiverOpt, backingField); @@ -3144,10 +3144,10 @@ private void VisitMethodBodies(BoundBlock blockBody, BoundBlock expressionBody) // In error cases we have two bodies. These are two unrelated pieces of code, // they are not executed one after another. As we don't really know which one the developer // intended to use, we need to visit both. We are going to pretend that there is - // an unconditional fork in execution and then we are converging after each body is executed. + // an unconditional fork in execution and then we are converging after each body is executed. // For example, if only one body assigns an out parameter, then after visiting both bodies // we should consider that parameter is not definitely assigned. - // Note, that today this code is not executed for regular definite assignment analysis. It is + // Note, that today this code is not executed for regular definite assignment analysis. It is // only executed for region analysis. TLocalState initialState = this.State.Clone(); Visit(blockBody); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index 7dc700b80e8..10edf68f437 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -584,11 +584,11 @@ protected virtual void NoteWrite(Symbol variable, BoundExpression value, bool re // unless the written value is always a constant. The reasons for this // unusual behavior are: // - // * The debugger does not make it easy to see the returned value of + // * The debugger does not make it easy to see the returned value of // a method. Often a call whose returned value would normally be // discarded is written into a local variable so that it can be // easily inspected in the debugger. - // + // // * An otherwise unread local variable that contains a reference to an // object can keep the object alive longer, particularly if the jitter // is not optimizing the lifetimes of locals. (Because, for example, @@ -597,7 +597,7 @@ protected virtual void NoteWrite(Symbol variable, BoundExpression value, bool re // the developer to more easily examine its state. // // * A developer who wishes to deliberately discard a value returned by - // a method can do so in a self-documenting manner via + // a method can do so in a self-documenting manner via // "var unread = M();" // // We suppress the "written but not read" message on locals unless what is @@ -625,7 +625,7 @@ internal static bool WriteConsideredUse(TypeSymbol type, BoundExpression value) if ((object)type != null && type.IsPointerOrFunctionPointer()) { - // We always suppress the warning for pointer types. + // We always suppress the warning for pointer types. return true; } @@ -798,7 +798,7 @@ protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundE if (Binder.AccessingAutoPropertyFromConstructor(propAccess, this.CurrentSymbol)) { var propSymbol = propAccess.PropertySymbol; - member = (propSymbol as SourcePropertySymbol)?.BackingField; + member = (propSymbol as SourcePropertySymbolBase)?.BackingField; if (member is null) { return false; @@ -1023,7 +1023,7 @@ private bool IsAssigned(BoundExpression node, out int unassignedSlot) if (Binder.AccessingAutoPropertyFromConstructor(propertyAccess, this.CurrentSymbol)) { var property = propertyAccess.PropertySymbol; - var backingField = (property as SourcePropertySymbol)?.BackingField; + var backingField = (property as SourcePropertySymbolBase)?.BackingField; if (backingField != null) { if (!MayRequireTracking(propertyAccess.ReceiverOpt, backingField) || IsAssigned(propertyAccess.ReceiverOpt, out unassignedSlot)) @@ -2036,7 +2036,7 @@ public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) if (Binder.AccessingAutoPropertyFromConstructor(node, this.CurrentSymbol)) { var property = node.PropertySymbol; - var backingField = (property as SourcePropertySymbol)?.BackingField; + var backingField = (property as SourcePropertySymbolBase)?.BackingField; if (backingField != null) { if (MayRequireTracking(node.ReceiverOpt, backingField)) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index cd2e671546e..e676ecbad85 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7039,14 +7039,14 @@ static FlowAnalysisAnnotations getSetterAnnotations(PropertySymbol property) { return accessor.Parameters.Last().FlowAnalysisAnnotations; } - if (property is SourcePropertySymbol sourceProperty) + if (property is SourcePropertySymbolBase sourceProperty) { return getPropertyAnnotations(sourceProperty); } return FlowAnalysisAnnotations.None; } - static FlowAnalysisAnnotations getPropertyAnnotations(SourcePropertySymbol property) + static FlowAnalysisAnnotations getPropertyAnnotations(SourcePropertySymbolBase property) { var annotations = FlowAnalysisAnnotations.None; if (property.HasAllowNull) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index 23a081e2b9b..b4cfb49bcde 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -300,7 +300,7 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo if (setMethod is null) { - var autoProp = (SourceOrRecordPropertySymbol)property; + var autoProp = (SourcePropertySymbolBase)property; Debug.Assert(autoProp.IsAutoProperty, "only autoproperties can be assignable without having setters"); diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 2973c5f824c..eeffec10f0f 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -104,7 +104,7 @@ private static bool ShouldMethodDisplayReadOnly(IMethodSymbol method, IPropertyS } if ((method as Symbols.PublicModel.MethodSymbol)?.UnderlyingMethodSymbol is SourcePropertyAccessorSymbol sourceAccessor && - (propertyOpt as Symbols.PublicModel.PropertySymbol)?.UnderlyingSymbol is SourcePropertySymbol sourceProperty) + (propertyOpt as Symbols.PublicModel.PropertySymbol)?.UnderlyingSymbol is SourcePropertySymbolBase sourceProperty) { // only display if the accessor is explicitly readonly return sourceAccessor.LocalDeclaredReadOnly || sourceProperty.HasReadOnlyModifier; @@ -319,7 +319,7 @@ public override void VisitMethod(IMethodSymbol symbol) } } - // Method members always have a type unless (1) this is a lambda method symbol, which we + // Method members always have a type unless (1) this is a lambda method symbol, which we // have dealt with already, or (2) this is an error method symbol. If we have an error method // symbol then we do not know its accessibility, modifiers, etc, all of which require knowing // the containing type, so we'll skip them. @@ -343,7 +343,7 @@ public override void VisitMethod(IMethodSymbol symbol) break; case MethodKind.Destructor: case MethodKind.Conversion: - // If we're using the metadata format, then include the return type. + // If we're using the metadata format, then include the return type. // Otherwise we eschew it since it is redundant in a conversion // signature. if (format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) @@ -354,8 +354,8 @@ public override void VisitMethod(IMethodSymbol symbol) break; default: // The display code is called by the debugger; if a developer is debugging Roslyn and attempts - // to visualize a symbol *during its construction*, the parameters and return type might - // still be null. + // to visualize a symbol *during its construction*, the parameters and return type might + // still be null. if (symbol.ReturnsByRef) { @@ -777,8 +777,8 @@ private void AddParametersIfRequired(bool hasThisParameter, bool isVarargs, Immu var first = true; // The display code is called by the debugger; if a developer is debugging Roslyn and attempts - // to visualize a symbol *during its construction*, the parameters and return type might - // still be null. + // to visualize a symbol *during its construction*, the parameters and return type might + // still be null. if (!parameters.IsDefault) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index fc641778b60..e374bf80d9d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -986,7 +986,7 @@ internal bool TryCalculateSyntaxOffsetOfPositionInInitializer(int position, Synt // |<-----------distanceFromCtorBody----------->| // [ initializer 0 ][ initializer 1 ][ initializer 2 ][ctor initializer][ctor body] // |<--preceding init len-->| ^ - // position + // position int initializersLength = isStatic ? membersAndInitializers.StaticInitializersSyntaxLength : membersAndInitializers.InstanceInitializersSyntaxLength; int distanceFromInitializerStart = position - siblingInitializers[index].Syntax.Span.Start; @@ -1255,7 +1255,7 @@ internal override IEnumerable GetFieldsToEmit() /// /// During early attribute decoding, we consider a safe subset of all members that will not /// cause cyclic dependencies. Get all such members for this symbol. - /// + /// /// In particular, this method will return nested types and fields (other than auto-property /// backing fields). /// @@ -1267,7 +1267,7 @@ internal override ImmutableArray GetEarlyAttributeDecodingMembers() /// /// During early attribute decoding, we consider a safe subset of all members that will not /// cause cyclic dependencies. Get all such members for this symbol that have a particular name. - /// + /// /// In particular, this method will return nested types and fields (other than auto-property /// backing fields). /// @@ -1478,23 +1478,23 @@ private void CheckMemberNameConflicts(DiagnosticBag diagnostics) // not need any special handling in this method. // // However, we must have special handling for conversions because conversions - // use a completely different rule for detecting collisions between two + // use a completely different rule for detecting collisions between two // conversions: conversion signatures consist only of the source and target // types of the conversions, and not the kind of the conversion (implicit or explicit), // the name of the method, and so on. // // Therefore we must detect the following kinds of member name conflicts: // - // 1. a method, conversion or field has the same name as a (different) field (* see note below) + // 1. a method, conversion or field has the same name as a (different) field (* see note below) // 2. a method has the same method signature as another method or conversion // 3. a conversion has the same conversion signature as another conversion. // - // However, we must *not* detect "a conversion has the same *method* signature - // as another conversion" because conversions are allowed to overload on + // However, we must *not* detect "a conversion has the same *method* signature + // as another conversion" because conversions are allowed to overload on // return type but methods are not. // // (*) NOTE: Throughout the rest of this method I will use "field" as a shorthand for - // "non-method, non-conversion, non-type member", rather than spelling out + // "non-method, non-conversion, non-type member", rather than spelling out // "field, property or event...") foreach (var pair in membersByName) @@ -1521,7 +1521,7 @@ private void CheckMemberNameConflicts(DiagnosticBag diagnostics) // * a field directly following a field // * a field directly following a method or conversion // - // Furthermore: we do not wish to detect collisions between nested types in + // Furthermore: we do not wish to detect collisions between nested types in // this code; that is tested elsewhere. However, we do wish to detect a collision // between a nested type and a field, method or conversion. Therefore we // initialize our "bad transition" detector with a type of the given name, @@ -1537,8 +1537,8 @@ private void CheckMemberNameConflicts(DiagnosticBag diagnostics) // // If either the current symbol or the "last symbol" are not methods then // there must be a collision: - // - // * if the current symbol is not a method and the last symbol is, then + // + // * if the current symbol is not a method and the last symbol is, then // there is a field directly following a method of the same name // * if the current symbol is a method and the last symbol is not, then // there is a method directly or indirectly following a field of the same name, @@ -1698,7 +1698,7 @@ private void CheckIndexerNameConflicts(DiagnosticBag diagnostics, Dictionary(MemberSignatureComparer.DuplicateSourceComparer); - // Note: Can't assume that all indexers are called WellKnownMemberNames.Indexer because + // Note: Can't assume that all indexers are called WellKnownMemberNames.Indexer because // they may be explicit interface implementations. foreach (var members in membersByName.Values) { @@ -1955,17 +1955,17 @@ private void CheckForProtectedInStaticClass(DiagnosticBag diagnostics) private void CheckForUnmatchedOperators(DiagnosticBag diagnostics) { // SPEC: The true and false unary operators require pairwise declaration. - // SPEC: A compile-time error occurs if a class or struct declares one + // SPEC: A compile-time error occurs if a class or struct declares one // SPEC: of these operators without also declaring the other. // // SPEC DEFICIENCY: The line of the specification quoted above should say // the same thing as the lines below: that the formal parameters of the - // paired true/false operators must match exactly. You can't do + // paired true/false operators must match exactly. You can't do // op true(S) and op false(S?) for example. // SPEC: Certain binary operators require pairwise declaration. For every // SPEC: declaration of either operator of a pair, there must be a matching - // SPEC: declaration of the other operator of the pair. Two operator + // SPEC: declaration of the other operator of the pair. Two operator // SPEC: declarations match when they have the same return type and the same // SPEC: type for each parameter. The following operators require pairwise // SPEC: declaration: == and !=, > and <, >= and <=. @@ -2987,9 +2987,6 @@ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilde diagnostics.Add(ErrorCode.ERR_BadRecordDeclaration, paramList.Location); } - BinderFactory binderFactory = this.DeclaringCompilation.GetBinderFactory(paramList.SyntaxTree); - var binder = binderFactory.GetBinder(paramList); - var ctor = addCtor(builder.RecordDeclarationWithParameters); var existingOrAddedMembers = addProperties(ctor.Parameters); addDeconstruct(ctor.Parameters, existingOrAddedMembers); @@ -3280,8 +3277,8 @@ private void AddSynthesizedConstructorsIfNecessary(ArrayBuilder members, } // constants don't count, since they do not exist as fields at runtime - // NOTE: even for decimal constants (which require field initializers), - // we do not create .cctor here since a static constructor implicitly created for a decimal + // NOTE: even for decimal constants (which require field initializers), + // we do not create .cctor here since a static constructor implicitly created for a decimal // should not appear in the list returned by public API like GetMembers(). if (!hasStaticConstructor && HasNonConstantInitializer(staticInitializers)) { @@ -3438,7 +3435,7 @@ private static bool HasNonConstantInitializer(ArrayBuilder - /// In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a + /// In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a /// base type from another assembly) it is necessary for the compiler to generate explicit implementations for /// some interface methods. They don't go in the symbol table, but if we are emitting, then we should /// generate code for them. @@ -96,9 +96,9 @@ private void CheckAbstractClassImplementations(DiagnosticBag diagnostics) { var synthesizedImplementations = ArrayBuilder.GetInstance(); - // NOTE: We can't iterator over this collection directly, since it is not ordered. Instead we - // iterate over AllInterfaces and filter out the interfaces that are not in this set. This is - // preferable to doing the DFS ourselves because both AllInterfaces and + // NOTE: We can't iterator over this collection directly, since it is not ordered. Instead we + // iterate over AllInterfaces and filter out the interfaces that are not in this set. This is + // preferable to doing the DFS ourselves because both AllInterfaces and // InterfacesAndTheirBaseInterfaces are cached and used in multiple places. MultiDictionary interfacesAndTheirBases = this.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics; @@ -215,8 +215,8 @@ private void CheckAbstractClassImplementations(DiagnosticBag diagnostics) ReportAccessorOfInterfacePropertyOrEvent(associatedPropertyOrEvent) || (wasImplementingMemberFound && !implementingMember.IsAccessor())) { - //we're here because - //(a) the interface member is not an accessor, or + //we're here because + //(a) the interface member is not an accessor, or //(b) the interface member is an accessor of an interesting (see ReportAccessorOfInterfacePropertyOrEvent) property or event, or //(c) the implementing member exists and is not an accessor. bool reportedAnError = false; @@ -289,14 +289,14 @@ private void CheckAbstractClassImplementations(DiagnosticBag diagnostics) { // Don't report use site errors on properties - we'll report them on each of their accessors. - // Don't report use site errors for implementations in other types unless + // Don't report use site errors for implementations in other types unless // a synthesized implementation is needed that invokes the base method. // We can do so only if there are no use-site errors. if ((object)synthesizedImplementation != null || TypeSymbol.Equals(implementingMember.ContainingType, this, TypeCompareKind.ConsiderEverything2)) { DiagnosticInfo useSiteDiagnostic = interfaceMember.GetUseSiteDiagnostic(); - // CAVEAT: don't report ERR_ByRefReturnUnsupported since by-ref return types are + // CAVEAT: don't report ERR_ByRefReturnUnsupported since by-ref return types are // specifically allowed for the purposes of interface implementation (for C++ interop). // However, if there's a reference to the interface member in source, then we do want // to produce a use site error. @@ -494,7 +494,7 @@ private void HasBaseInterfaceDeclaringInterface(NamedTypeSymbol baseInterface, N case TypeKind.Class: case TypeKind.Struct: case TypeKind.Interface: - case TypeKind.Submission: // we have to check that "override" is not used + case TypeKind.Submission: // we have to check that "override" is not used break; default: @@ -566,7 +566,7 @@ private void HasBaseInterfaceDeclaringInterface(NamedTypeSymbol baseInterface, N } } } - else if (property is SourcePropertySymbol sourceProperty) + else if (property is SourcePropertySymbolBase sourceProperty) { var isNewProperty = sourceProperty.IsNew; CheckNonOverrideMember(property, isNewProperty, property.OverriddenOrHiddenMembers, diagnostics, out suppressAccessors); @@ -813,11 +813,11 @@ private void CheckNewModifier(Symbol symbol, bool isNew, DiagnosticBag diagnosti } // From: SymbolPreparer.cpp - // DevDiv Bugs 115384: Both out and ref parameters are implemented as references. In addition, out parameters are + // DevDiv Bugs 115384: Both out and ref parameters are implemented as references. In addition, out parameters are // decorated with OutAttribute. In CLR when a signature is looked up in virtual dispatch, CLR does not distinguish - // between these to parameter types. The choice is the last method in the vtable. Therefore we check and warn if - // there would potentially be a mismatch in CLRs and C#s choice of the overridden method. Unfortunately we have no - // way of communicating to CLR which method is the overridden one. We only run into this problem when the + // between these to parameter types. The choice is the last method in the vtable. Therefore we check and warn if + // there would potentially be a mismatch in CLRs and C#s choice of the overridden method. Unfortunately we have no + // way of communicating to CLR which method is the overridden one. We only run into this problem when the // parameters are generic. var runtimeOverriddenMembers = overriddenOrHiddenMembers.RuntimeOverriddenMembers; Debug.Assert(!runtimeOverriddenMembers.IsDefault); @@ -1544,19 +1544,19 @@ private SynthesizedExplicitImplementationForwardingMethod SynthesizeInterfaceMem /// 1) declares that it implements that interface; or /// 2) is a base class of a type that declares that it implements the interface but not /// a subtype of a class that declares that it implements the interface. - /// + /// /// For example, - /// + /// /// interface I /// class A /// class B : A, I /// class C : B /// class D : C, I - /// - /// Suppose the runtime is looking for D's implementation of a member of I. It will look in + /// + /// Suppose the runtime is looking for D's implementation of a member of I. It will look in /// D because of (1), will not look in C, will look in B because of (1), and will look in A /// because of (2). - /// + /// /// The key point is that it does not look in C, which C# *does*. /// private static bool IsPossibleImplementationUnderRuntimeRules(MethodSymbol implementingMethod, NamedTypeSymbol @interface) @@ -1577,7 +1577,7 @@ private static bool IsPossibleImplementationUnderRuntimeRules(MethodSymbol imple /// /// /// This is based on SymbolPreparer::IsCLRMethodImplSame in the native compiler. - /// + /// /// ACASEY: What the native compiler actually does is compute the C# answer, compute the CLR answer, /// and then confirm that they override the same method. What I've done here is check for the situations /// where the answers could disagree. I believe the results will be equivalent. If in doubt, a more conservative diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs deleted file mode 100644 index 37f0054257c..00000000000 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs +++ /dev/null @@ -1,38 +0,0 @@ -๏ปฟ// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.CodeAnalysis.CSharp.Symbols -{ - internal abstract class SourceOrRecordPropertySymbol : PropertySymbol, IAttributeTargetSymbol - { - public Location Location { get; } - - public SourceOrRecordPropertySymbol(Location location) - { - Location = location; - } - - internal abstract SynthesizedBackingFieldSymbol BackingField { get; } - - internal abstract bool IsAutoProperty { get; } - - internal abstract bool HasPointerType { get; } - - public abstract SyntaxList AttributeDeclarationSyntaxList { get; } - - protected abstract IAttributeTargetSymbol AttributesOwner { get; } - - protected abstract AttributeLocation AllowedAttributeLocations { get; } - - protected abstract AttributeLocation DefaultAttributeLocation { get; } - - IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => AttributesOwner; - - AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AllowedAttributeLocations; - - AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => DefaultAttributeLocation; - } -} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 7bd24deef3f..d460ceccfa2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol { - private readonly SourcePropertySymbol _property; + private readonly SourcePropertySymbolBase _property; private ImmutableArray _lazyParameters; private TypeWithAnnotations _lazyReturnType; private ImmutableArray _lazyRefCustomModifiers; @@ -53,6 +53,10 @@ internal sealed class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol out explicitInterfaceImplementations); var methodKind = isGetMethod ? MethodKind.PropertyGet : MethodKind.PropertySet; + + bool hasBody = syntax.Body is object; + bool hasExpressionBody = syntax.ExpressionBody is object; + CheckForBlockAndExpressionBody(syntax.Body, syntax.ExpressionBody, syntax, diagnostics); return new SourcePropertyAccessorSymbol( containingType, name, @@ -61,7 +65,12 @@ internal sealed class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol explicitInterfaceImplementations, syntax.Keyword.GetLocation(), syntax, + hasBody, + hasExpressionBody, + isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), + syntax.Modifiers, methodKind, + syntax.Keyword.IsKind(SyntaxKind.InitKeyword), isAutoPropertyAccessor, isExplicitInterfaceImplementation, diagnostics); @@ -102,6 +111,50 @@ internal sealed class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol diagnostics); } +#nullable enable + public static SourcePropertyAccessorSymbol CreateAccessorSymbol( + bool isGetMethod, + bool usesInit, + NamedTypeSymbol containingType, + SynthesizedRecordPropertySymbol property, + DeclarationModifiers propertyModifiers, + string propertyName, + Location location, + CSharpSyntaxNode syntax, + DiagnosticBag diagnostics) + { + string name; + ImmutableArray explicitInterfaceImplementations; + GetNameAndExplicitInterfaceImplementations( + explicitlyImplementedPropertyOpt: null, + propertyName, + property.IsCompilationOutputWinMdObj(), + aliasQualifierOpt: null, + isGetMethod, + out name, + out explicitInterfaceImplementations); + + var methodKind = isGetMethod ? MethodKind.PropertyGet : MethodKind.PropertySet; + return new SourcePropertyAccessorSymbol( + containingType, + name, + property, + propertyModifiers, + explicitInterfaceImplementations, + location, + syntax, + hasBody: false, + hasExpressionBody: false, + isIterator: false, + modifiers: new SyntaxTokenList(), + methodKind, + usesInit, + isAutoPropertyAccessor: true, + isExplicitInterfaceImplementation: false, + diagnostics); + } +#nullable restore + internal override bool IsExpressionBodied => _isExpressionBodied; @@ -197,39 +250,43 @@ internal sealed override ImmutableArray NotNullWhenFalseMembers } } +#nullable enable private SourcePropertyAccessorSymbol( NamedTypeSymbol containingType, string name, - SourcePropertySymbol property, + SourcePropertySymbolBase property, DeclarationModifiers propertyModifiers, ImmutableArray explicitInterfaceImplementations, Location location, - AccessorDeclarationSyntax syntax, + CSharpSyntaxNode syntax, + bool hasBody, + bool hasExpressionBody, + bool isIterator, + SyntaxTokenList modifiers, MethodKind methodKind, + bool usesInit, bool isAutoPropertyAccessor, bool isExplicitInterfaceImplementation, DiagnosticBag diagnostics) : base(containingType, syntax.GetReference(), location, - isIterator: SyntaxFacts.HasYieldOperations(syntax.Body)) + isIterator: isIterator) { _property = property; _explicitInterfaceImplementations = explicitInterfaceImplementations; _name = name; _isAutoPropertyAccessor = isAutoPropertyAccessor; Debug.Assert(!_property.IsExpressionBodied, "Cannot have accessors in expression bodied lightweight properties"); - var hasBody = syntax.Body != null; - var hasExpressionBody = syntax.ExpressionBody != null; _isExpressionBodied = !hasBody && hasExpressionBody; - _usesInit = syntax.Keyword.IsKind(SyntaxKind.InitKeyword); + _usesInit = usesInit; if (_usesInit) { - Binder.CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInitOnlySetters, diagnostics, syntax.Keyword.GetLocation()); + Binder.CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInitOnlySetters, diagnostics, location); } bool modifierErrors; - var declarationModifiers = this.MakeModifiers(syntax, isExplicitInterfaceImplementation, hasBody || hasExpressionBody, location, diagnostics, out modifierErrors); + var declarationModifiers = this.MakeModifiers(modifiers, isExplicitInterfaceImplementation, hasBody || hasExpressionBody, location, diagnostics, out modifierErrors); // Include some modifiers from the containing property, but not the accessibility modifiers. declarationModifiers |= GetAccessorModifiers(propertyModifiers) & ~DeclarationModifiers.AccessibilityMask; @@ -274,9 +331,8 @@ internal sealed override ImmutableArray NotNullWhenFalseMembers } } - CheckForBlockAndExpressionBody( - syntax.Body, syntax.ExpressionBody, syntax, diagnostics); } +#nullable restore private static DeclarationModifiers GetAccessorModifiers(DeclarationModifiers propertyModifiers) => propertyModifiers & ~(DeclarationModifiers.Indexer | DeclarationModifiers.ReadOnly); @@ -299,7 +355,7 @@ protected override void MethodChecks(DiagnosticBag diagnostics) } else if (this.IsOverride) { - // This will cause another call to SourceMethodSymbol.LazyMethodChecks, + // This will cause another call to SourceMethodSymbol.LazyMethodChecks, // but that method already handles reentrancy for exactly this case. MethodSymbol overriddenMethod = this.OverriddenMethod; if ((object)overriddenMethod != null) @@ -525,7 +581,7 @@ internal override bool IsDeclaredReadOnly internal override bool IsInitOnly => !IsStatic && _usesInit; - private DeclarationModifiers MakeModifiers(AccessorDeclarationSyntax syntax, bool isExplicitInterfaceImplementation, + private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool isExplicitInterfaceImplementation, bool hasBody, Location location, DiagnosticBag diagnostics, out bool modifierErrors) { // No default accessibility. If unset, accessibility @@ -546,7 +602,7 @@ internal override bool IsDeclaredReadOnly defaultInterfaceImplementationModifiers = DeclarationModifiers.AccessibilityMask; } - var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); ModifierUtils.ReportDefaultInterfaceImplementationModifiers(hasBody, mods, defaultInterfaceImplementationModifiers, @@ -610,7 +666,8 @@ internal static string GetAccessorName(string propertyName, bool getNotSet, bool } /// - /// or + /// The declaring syntax for the accessor, or property if there is no accessor-specific + /// syntax. /// internal CSharpSyntaxNode GetSyntax() { @@ -661,8 +718,17 @@ public override bool IsImplicitlyDeclared get { // Per design meeting resolution [see bug 11253], no source accessor is implicitly declared in C#, - // because there is "get", "set", or expression-body syntax. - return false; + // if there is "get", "set", or expression-body syntax. + switch (GetSyntax().Kind()) + { + case SyntaxKind.GetAccessorDeclaration: + case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: + case SyntaxKind.ArrowExpressionClause: + return false; + }; + + return true; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 53206e2e027..21a5af29642 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -2,67 +2,33 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; +#nullable enable + using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal sealed class SourcePropertySymbol : SourceOrRecordPropertySymbol + internal sealed class SourcePropertySymbol : SourcePropertySymbolBase { - /// - /// Condensed flags storing useful information about the - /// so that we do not have to go back to source to compute this data. - /// - [Flags] - private enum Flags : byte - { - IsExpressionBodied = 1 << 0, - IsAutoProperty = 1 << 1, - IsExplicitInterfaceImplementation = 1 << 2, - } - private const string DefaultIndexerName = "Item"; - // TODO (tomat): consider splitting into multiple subclasses/rare data. - - private readonly SourceMemberContainerTypeSymbol _containingType; - private readonly string _name; - private readonly SyntaxReference _syntaxRef; - private readonly DeclarationModifiers _modifiers; - private readonly ImmutableArray _refCustomModifiers; - private readonly SourcePropertyAccessorSymbol _getMethod; - private readonly SourcePropertyAccessorSymbol _setMethod; - private readonly TypeSymbol _explicitInterfaceType; - private readonly ImmutableArray _explicitInterfaceImplementations; - private readonly Flags _propertyFlags; - private readonly RefKind _refKind; - - private SymbolCompletionState _state; - private ImmutableArray _lazyParameters; - private TypeWithAnnotations.Boxed _lazyType; - - /// - /// Set in constructor, might be changed while decoding . - /// - private readonly string _sourceName; - - private string _lazyDocComment; - private string _lazyExpandedDocComment; - private OverriddenOrHiddenMembersResult _lazyOverriddenOrHiddenMembers; - private SynthesizedSealedPropertyAccessor _lazySynthesizedSealedAccessor; - private CustomAttributesBag _lazyCustomAttributesBag; + internal static SourcePropertySymbol Create(SourceMemberContainerTypeSymbol containingType, Binder bodyBinder, PropertyDeclarationSyntax syntax, DiagnosticBag diagnostics) + { + var nameToken = syntax.Identifier; + var location = nameToken.GetLocation(); + return new SourcePropertySymbol(containingType, bodyBinder, syntax, nameToken.ValueText, location, diagnostics); + } - // CONSIDER: if the parameters were computed lazily, ParameterCount could be overridden to fall back on the syntax (as in SourceMemberMethodSymbol). + internal static SourcePropertySymbol Create(SourceMemberContainerTypeSymbol containingType, Binder bodyBinder, IndexerDeclarationSyntax syntax, DiagnosticBag diagnostics) + { + var location = syntax.ThisKeyword.GetLocation(); + return new SourcePropertySymbol(containingType, bodyBinder, syntax, DefaultIndexerName, location, diagnostics); + } private SourcePropertySymbol( SourceMemberContainerTypeSymbol containingType, @@ -71,378 +37,53 @@ private enum Flags : byte string name, Location location, DiagnosticBag diagnostics) - : base(location) + : base(containingType, bodyBinder, syntax, syntax.Type.GetRefKind(), name, location, diagnostics) { - // This has the value that IsIndexer will ultimately have, once we've populated the fields of this object. - bool isIndexer = syntax.Kind() == SyntaxKind.IndexerDeclaration; - var interfaceSpecifier = GetExplicitInterfaceSpecifier(syntax); - bool isExplicitInterfaceImplementation = interfaceSpecifier != null; - if (isExplicitInterfaceImplementation) - { - _propertyFlags |= Flags.IsExplicitInterfaceImplementation; - } - - _containingType = containingType; - _syntaxRef = syntax.GetReference(); - _refKind = syntax.Type.GetRefKind(); - - SyntaxTokenList modifiers = syntax.Modifiers; - bodyBinder = bodyBinder.WithUnsafeRegionIfNecessary(modifiers); - bodyBinder = bodyBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); - - var propertySyntax = syntax as PropertyDeclarationSyntax; - var arrowExpression = propertySyntax != null - ? propertySyntax.ExpressionBody - : ((IndexerDeclarationSyntax)syntax).ExpressionBody; - bool hasExpressionBody = arrowExpression != null; - bool hasInitializer = !isIndexer && propertySyntax.Initializer != null; - - GetAcessorDeclarations(syntax, diagnostics, out bool isAutoProperty, out bool hasAccessorList, - out AccessorDeclarationSyntax getSyntax, out AccessorDeclarationSyntax setSyntax); - - bool accessorsHaveImplementation; - if (hasAccessorList) - { - accessorsHaveImplementation = (getSyntax != null && (getSyntax.Body != null || getSyntax.ExpressionBody != null)) || - (setSyntax != null && (setSyntax.Body != null || setSyntax.ExpressionBody != null)); - } - else - { - accessorsHaveImplementation = hasExpressionBody; - } - - bool modifierErrors; - _modifiers = MakeModifiers(modifiers, isExplicitInterfaceImplementation, isIndexer, - accessorsHaveImplementation, location, - diagnostics, out modifierErrors); - this.CheckAccessibility(location, diagnostics, isExplicitInterfaceImplementation); - - this.CheckModifiers(isExplicitInterfaceImplementation, location, isIndexer, diagnostics); - - isAutoProperty = isAutoProperty && (!(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer && hasAccessorList); - - if (isIndexer && !isExplicitInterfaceImplementation) - { - // Evaluate the attributes immediately in case the IndexerNameAttribute has been applied. - // NOTE: we want IsExplicitInterfaceImplementation, IsOverride, Locations, and the syntax reference - // to be initialized before we pass this symbol to LoadCustomAttributes. - - // CONSIDER: none of the information from this early binding pass is cached. Everything will - // be re-bound when someone calls GetAttributes. If this gets to be a problem, we could - // always use the real attribute bag of this symbol and modify LoadAndValidateAttributes to - // handle partially filled bags. - CustomAttributesBag temp = null; - LoadAndValidateAttributes(OneOrMany.Create(this.CSharpSyntaxNode.AttributeLists), ref temp, earlyDecodingOnly: true); - if (temp != null) - { - Debug.Assert(temp.IsEarlyDecodedWellKnownAttributeDataComputed); - var propertyData = (PropertyEarlyWellKnownAttributeData)temp.EarlyDecodedWellKnownAttributeData; - if (propertyData != null) - { - _sourceName = propertyData.IndexerName; - } - } - } - - string aliasQualifierOpt; - string memberName = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(bodyBinder, interfaceSpecifier, name, diagnostics, out _explicitInterfaceType, out aliasQualifierOpt); - _sourceName = _sourceName ?? memberName; //sourceName may have been set while loading attributes - _name = isIndexer ? ExplicitInterfaceHelpers.GetMemberName(WellKnownMemberNames.Indexer, _explicitInterfaceType, aliasQualifierOpt) : _sourceName; - - if (hasInitializer) - { - CheckInitializer(isAutoProperty, containingType.IsInterface, IsStatic, location, diagnostics); - } - - if (isAutoProperty || hasInitializer) - { - var hasGetSyntax = getSyntax != null; - var isAutoPropertyWithGetSyntax = isAutoProperty && hasGetSyntax; - if (isAutoPropertyWithGetSyntax) - { - _propertyFlags |= Flags.IsAutoProperty; - } - - bool isGetterOnly = hasGetSyntax && setSyntax == null; - - if (isAutoPropertyWithGetSyntax && !IsStatic && !isGetterOnly) - { - if (ContainingType.IsReadOnly) - { - diagnostics.Add(ErrorCode.ERR_AutoPropsInRoStruct, location); - } - else if (HasReadOnlyModifier) - { - diagnostics.Add(ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly, location, this); - } - } - - if (isAutoPropertyWithGetSyntax || hasInitializer) - { - if (isAutoPropertyWithGetSyntax) - { - //issue a diagnostic if the compiler generated attribute ctor is not found. - Binder.ReportUseSiteDiagnosticForSynthesizedAttribute(bodyBinder.Compilation, - WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor, diagnostics, syntax: syntax); - - if (this._refKind != RefKind.None && !_containingType.IsInterface) - { - diagnostics.Add(ErrorCode.ERR_AutoPropertyCannotBeRefReturning, location, this); - } - } - - string fieldName = GeneratedNames.MakeBackingFieldName(_sourceName); - bool isInitOnly = !IsStatic && setSyntax?.Keyword.IsKind(SyntaxKind.InitKeyword) == true; - BackingField = new SynthesizedBackingFieldSymbol(this, - fieldName, - isReadOnly: isGetterOnly || isInitOnly, - this.IsStatic, - hasInitializer); - } - - if (isAutoProperty) - { - Binder.CheckFeatureAvailability( - syntax, - isGetterOnly ? MessageID.IDS_FeatureReadonlyAutoImplementedProperties : MessageID.IDS_FeatureAutoImplementedProperties, - diagnostics, - location); - } - } - - PropertySymbol explicitlyImplementedProperty = null; - _refCustomModifiers = ImmutableArray.Empty; - - // The runtime will not treat the accessors of this property as overrides or implementations - // of those of another property unless both the signatures and the custom modifiers match. - // Hence, in the case of overrides and *explicit* implementations, we need to copy the custom - // modifiers that are in the signatures of the overridden/implemented property accessors. - // (From source, we know that there can only be one overridden/implemented property, so there - // are no conflicts.) This is unnecessary for implicit implementations because, if the custom - // modifiers don't match, we'll insert bridge methods for the accessors (explicit implementations - // that delegate to the implicit implementations) with the correct custom modifiers - // (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation). - - // Note: we're checking if the syntax indicates explicit implementation rather, - // than if explicitInterfaceType is null because we don't want to look for an - // overridden property if this is supposed to be an explicit implementation. - if (isExplicitInterfaceImplementation || this.IsOverride) - { - // Type and parameters for overrides and explicit implementations cannot be bound - // lazily since the property name depends on the metadata name of the base property, - // and the property name is required to add the property to the containing type, and - // the type and parameters are required to determine the override or implementation. - var type = this.ComputeType(bodyBinder, syntax, diagnostics); - _lazyType = new TypeWithAnnotations.Boxed(type); - _lazyParameters = this.ComputeParameters(bodyBinder, syntax, diagnostics); - - bool isOverride = false; - PropertySymbol overriddenOrImplementedProperty = null; - - if (!isExplicitInterfaceImplementation) - { - // If this property is an override, we may need to copy custom modifiers from - // the overridden property (so that the runtime will recognize it as an override). - // We check for this case here, while we can still modify the parameters and - // return type without losing the appearance of immutability. - isOverride = true; - overriddenOrImplementedProperty = this.OverriddenProperty; - } - else - { - string interfacePropertyName = isIndexer ? WellKnownMemberNames.Indexer : name; - explicitlyImplementedProperty = this.FindExplicitlyImplementedProperty(_explicitInterfaceType, interfacePropertyName, interfaceSpecifier, diagnostics); - this.FindExplicitlyImplementedMemberVerification(explicitlyImplementedProperty, diagnostics); - overriddenOrImplementedProperty = explicitlyImplementedProperty; - } - - if ((object)overriddenOrImplementedProperty != null) - { - _refCustomModifiers = _refKind != RefKind.None ? overriddenOrImplementedProperty.RefCustomModifiers : ImmutableArray.Empty; - - TypeWithAnnotations overriddenPropertyType = overriddenOrImplementedProperty.TypeWithAnnotations; - - // We do an extra check before copying the type to handle the case where the overriding - // property (incorrectly) has a different type than the overridden property. In such cases, - // we want to retain the original (incorrect) type to avoid hiding the type given in source. - if (type.Type.Equals(overriddenPropertyType.Type, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamic)) - { - type = type.WithTypeAndModifiers( - CustomModifierUtils.CopyTypeCustomModifiers(overriddenPropertyType.Type, type.Type, this.ContainingAssembly), - overriddenPropertyType.CustomModifiers); - - _lazyType = new TypeWithAnnotations.Boxed(type); - } - - _lazyParameters = CustomModifierUtils.CopyParameterCustomModifiers(overriddenOrImplementedProperty.Parameters, _lazyParameters, alsoCopyParamsModifier: isOverride); - } - } - else if (_refKind == RefKind.RefReadOnly) - { - var modifierType = bodyBinder.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_InAttribute, diagnostics, syntax.Type); - - _refCustomModifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)); - } - - if (!hasAccessorList) - { - if (hasExpressionBody) - { - _propertyFlags |= Flags.IsExpressionBodied; - _getMethod = SourcePropertyAccessorSymbol.CreateAccessorSymbol( - containingType, - this, - _modifiers, - _sourceName, - arrowExpression, - explicitlyImplementedProperty, - aliasQualifierOpt, - isExplicitInterfaceImplementation, - diagnostics); - } - else - { - _getMethod = null; - } - _setMethod = null; - } - else - { - _getMethod = CreateAccessorSymbol(getSyntax, explicitlyImplementedProperty, aliasQualifierOpt, isAutoProperty, isExplicitInterfaceImplementation, diagnostics); - _setMethod = CreateAccessorSymbol(setSyntax, explicitlyImplementedProperty, aliasQualifierOpt, isAutoProperty, isExplicitInterfaceImplementation, diagnostics); - - if ((getSyntax == null) || (setSyntax == null)) - { - if ((getSyntax == null) && (setSyntax == null)) - { - diagnostics.Add(ErrorCode.ERR_PropertyWithNoAccessors, location, this); - } - else if (_refKind != RefKind.None) - { - if (getSyntax == null) - { - diagnostics.Add(ErrorCode.ERR_RefPropertyMustHaveGetAccessor, location, this); - } - } - else if (isAutoProperty) - { - var accessor = _getMethod ?? _setMethod; - if (getSyntax == null) - { - diagnostics.Add(ErrorCode.ERR_AutoPropertyMustHaveGetAccessor, accessor.Locations[0], accessor); - } - } - } - - // Check accessor accessibility is more restrictive than property accessibility. - CheckAccessibilityMoreRestrictive(_getMethod, diagnostics); - CheckAccessibilityMoreRestrictive(_setMethod, diagnostics); - - if (((object)_getMethod != null) && ((object)_setMethod != null)) - { - if (_refKind != RefKind.None) - { - diagnostics.Add(ErrorCode.ERR_RefPropertyCannotHaveSetAccessor, _setMethod.Locations[0], _setMethod); - } - else if ((_getMethod.LocalAccessibility != Accessibility.NotApplicable) && - (_setMethod.LocalAccessibility != Accessibility.NotApplicable)) - { - // Check accessibility is set on at most one accessor. - diagnostics.Add(ErrorCode.ERR_DuplicatePropertyAccessMods, location, this); - } - else if (_getMethod.LocalDeclaredReadOnly && _setMethod.LocalDeclaredReadOnly) - { - diagnostics.Add(ErrorCode.ERR_DuplicatePropertyReadOnlyMods, location, this); - } - else if (this.IsAbstract) - { - // Check abstract property accessors are not private. - CheckAbstractPropertyAccessorNotPrivate(_getMethod, diagnostics); - CheckAbstractPropertyAccessorNotPrivate(_setMethod, diagnostics); - } - } - else - { - if (!this.IsOverride) - { - var accessor = _getMethod ?? _setMethod; - if ((object)accessor != null) - { - // Check accessibility is not set on the one accessor. - if (accessor.LocalAccessibility != Accessibility.NotApplicable) - { - diagnostics.Add(ErrorCode.ERR_AccessModMissingAccessor, location, this); - } - - // Check that 'readonly' is not set on the one accessor. - if (accessor.LocalDeclaredReadOnly) - { - diagnostics.Add(ErrorCode.ERR_ReadOnlyModMissingAccessor, location, this); - } - } - } - } - } - - if ((object)explicitlyImplementedProperty != null) - { - CheckExplicitImplementationAccessor(this.GetMethod, explicitlyImplementedProperty.GetMethod, explicitlyImplementedProperty, diagnostics); - CheckExplicitImplementationAccessor(this.SetMethod, explicitlyImplementedProperty.SetMethod, explicitlyImplementedProperty, diagnostics); - } - - _explicitInterfaceImplementations = - (object)explicitlyImplementedProperty == null ? - ImmutableArray.Empty : - ImmutableArray.Create(explicitlyImplementedProperty); + } - // get-only auto property should not override settable properties - if ((_propertyFlags & Flags.IsAutoProperty) != 0) - { - if (_setMethod is null && !this.IsReadOnly) - { - diagnostics.Add(ErrorCode.ERR_AutoPropertyMustOverrideSet, location, this); - } + private TypeSyntax GetTypeSyntax(SyntaxNode syntax) => ((BasePropertyDeclarationSyntax)syntax).Type; - CheckForFieldTargetedAttribute(syntax, diagnostics); - } + protected override Location TypeLocation + => GetTypeSyntax(CSharpSyntaxNode).Location; - CheckForBlockAndExpressionBody( - syntax.AccessorList, syntax.GetExpressionBodySyntax(), syntax, diagnostics); - } + protected override SyntaxTokenList GetModifierTokens(SyntaxNode syntax) + => ((BasePropertyDeclarationSyntax)syntax).Modifiers; - private void CheckForFieldTargetedAttribute(BasePropertyDeclarationSyntax syntax, DiagnosticBag diagnostics) - { - var languageVersion = this.DeclaringCompilation.LanguageVersion; - if (languageVersion.AllowAttributesOnBackingFields()) + protected override ArrowExpressionClauseSyntax? GetArrowExpression(SyntaxNode syntax) + => syntax switch { - return; - } + PropertyDeclarationSyntax p => p.ExpressionBody, + IndexerDeclarationSyntax i => i.ExpressionBody, + _ => throw ExceptionUtilities.UnexpectedValue(syntax.Kind()) + }; - foreach (var attribute in syntax.AttributeLists) - { - if (attribute.Target?.GetAttributeLocation() == AttributeLocation.Field) - { - diagnostics.Add( - new CSDiagnosticInfo(ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable, - languageVersion.ToDisplayString(), - new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureAttributesOnBackingFields.RequiredVersion())), - attribute.Target.Location); - } - } - } + protected override bool HasInitializer(SyntaxNode syntax) + => syntax is PropertyDeclarationSyntax { Initializer: { } }; - private static void GetAcessorDeclarations(BasePropertyDeclarationSyntax syntax, DiagnosticBag diagnostics, - out bool isAutoProperty, out bool hasAccessorList, - out AccessorDeclarationSyntax getSyntax, out AccessorDeclarationSyntax setSyntax) - { + public override SyntaxList AttributeDeclarationSyntaxList + => ((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists; + + protected override void GetAccessorDeclarations( + CSharpSyntaxNode syntaxNode, + DiagnosticBag diagnostics, + out bool isAutoProperty, + out bool hasAccessorList, + out bool accessorsHaveImplementation, + out bool isInitOnly, + out CSharpSyntaxNode? getSyntax, + out CSharpSyntaxNode? setSyntax) + { + var syntax = (BasePropertyDeclarationSyntax)syntaxNode; isAutoProperty = true; hasAccessorList = syntax.AccessorList != null; getSyntax = null; setSyntax = null; + isInitOnly = false; if (hasAccessorList) { - foreach (var accessor in syntax.AccessorList.Accessors) + accessorsHaveImplementation = false; + foreach (var accessor in syntax.AccessorList!.Accessors) { switch (accessor.Kind()) { @@ -461,6 +102,10 @@ private void CheckForFieldTargetedAttribute(BasePropertyDeclarationSyntax syntax if (setSyntax == null) { setSyntax = accessor; + if (accessor.Keyword.IsKind(SyntaxKind.InitKeyword)) + { + isInitOnly = true; + } } else { @@ -482,466 +127,196 @@ private void CheckForFieldTargetedAttribute(BasePropertyDeclarationSyntax syntax if (accessor.Body != null || accessor.ExpressionBody != null) { isAutoProperty = false; + accessorsHaveImplementation = true; } } } else { isAutoProperty = false; + accessorsHaveImplementation = GetArrowExpression(syntax) is object; } } - internal sealed override ImmutableArray NotNullMembers => - GetDecodedWellKnownAttributeData()?.NotNullMembers ?? ImmutableArray.Empty; - - internal sealed override ImmutableArray NotNullWhenTrueMembers => - GetDecodedWellKnownAttributeData()?.NotNullWhenTrueMembers ?? ImmutableArray.Empty; - - internal sealed override ImmutableArray NotNullWhenFalseMembers => - GetDecodedWellKnownAttributeData()?.NotNullWhenFalseMembers ?? ImmutableArray.Empty; - - internal bool IsExpressionBodied - => (_propertyFlags & Flags.IsExpressionBodied) != 0; - - private void CheckInitializer( - bool isAutoProperty, - bool isInterface, - bool isStatic, - Location location, - DiagnosticBag diagnostics) + protected override void CheckForBlockAndExpressionBody(CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { - if (isInterface && !isStatic) - { - diagnostics.Add(ErrorCode.ERR_InstancePropertyInitializerInInterface, location, this); - } - else if (!isAutoProperty) - { - diagnostics.Add(ErrorCode.ERR_InitializerOnNonAutoProperty, location, this); - } + var prop = (BasePropertyDeclarationSyntax)syntax; + CheckForBlockAndExpressionBody( + prop.AccessorList, + prop.GetExpressionBodySyntax(), + prop, + diagnostics); } - internal static SourcePropertySymbol Create(SourceMemberContainerTypeSymbol containingType, Binder bodyBinder, PropertyDeclarationSyntax syntax, DiagnosticBag diagnostics) + protected override DeclarationModifiers MakeModifiers( + SyntaxTokenList modifiers, + bool isExplicitInterfaceImplementation, + bool isIndexer, + bool accessorsHaveImplementation, + Location location, + DiagnosticBag diagnostics, + out bool modifierErrors) { - var nameToken = syntax.Identifier; - var location = nameToken.GetLocation(); - return new SourcePropertySymbol(containingType, bodyBinder, syntax, nameToken.ValueText, location, diagnostics); - } + bool isInterface = this.ContainingType.IsInterface; + var defaultAccess = isInterface && !isExplicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; - internal static SourcePropertySymbol Create(SourceMemberContainerTypeSymbol containingType, Binder bodyBinder, IndexerDeclarationSyntax syntax, DiagnosticBag diagnostics) - { - var location = syntax.ThisKeyword.GetLocation(); - return new SourcePropertySymbol(containingType, bodyBinder, syntax, DefaultIndexerName, location, diagnostics); - } + // Check that the set of modifiers is allowed + var allowedModifiers = DeclarationModifiers.Unsafe; + var defaultInterfaceImplementationModifiers = DeclarationModifiers.None; - public override RefKind RefKind - { - get + if (!isExplicitInterfaceImplementation) { - return _refKind; - } - } + allowedModifiers |= DeclarationModifiers.New | + DeclarationModifiers.Sealed | + DeclarationModifiers.Abstract | + DeclarationModifiers.Virtual | + DeclarationModifiers.AccessibilityMask; - public override TypeWithAnnotations TypeWithAnnotations - { - get - { - if (_lazyType == null) + if (!isIndexer) { - var diagnostics = DiagnosticBag.GetInstance(); - var binder = this.CreateBinderForTypeAndParameters(); - var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); - var result = this.ComputeType(binder, syntax, diagnostics); - if (Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(result), null) == null) - { - this.AddDeclarationDiagnostics(diagnostics); - } - diagnostics.Free(); + allowedModifiers |= DeclarationModifiers.Static; } - return _lazyType.Value; - } - } - - internal override bool HasPointerType - { - get - { - if (_lazyType != null) + if (!isInterface) { - - var hasPointerType = _lazyType.Value.DefaultType.IsPointerOrFunctionPointer(); - Debug.Assert(hasPointerType == hasPointerTypeSyntactically()); - return hasPointerType; + allowedModifiers |= DeclarationModifiers.Override; } - - return hasPointerTypeSyntactically(); - - bool hasPointerTypeSyntactically() + else { - var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); - var typeSyntax = syntax.Type.SkipRef(out _); - return typeSyntax.Kind() switch { SyntaxKind.PointerType => true, SyntaxKind.FunctionPointerType => true, _ => false }; + // This is needed to make sure we can detect 'public' modifier specified explicitly and + // check it against language version below. + defaultAccess = DeclarationModifiers.None; + + defaultInterfaceImplementationModifiers |= DeclarationModifiers.Sealed | + DeclarationModifiers.Abstract | + (isIndexer ? 0 : DeclarationModifiers.Static) | + DeclarationModifiers.Virtual | + DeclarationModifiers.Extern | + DeclarationModifiers.AccessibilityMask; } } - } - - /// - /// To facilitate lookup, all indexer symbols have the same name. - /// Check the MetadataName property to find the name that will be - /// emitted (based on IndexerNameAttribute, or the default "Item"). - /// - public override string Name - { - get + else if (isInterface) { - return _name; + Debug.Assert(isExplicitInterfaceImplementation); + allowedModifiers |= DeclarationModifiers.Abstract; } - } - public override string MetadataName - { - get + if (ContainingType.IsStructType()) { - // Explicit implementation names may have spaces if the interface - // is generic (between the type arguments). - return _sourceName.Replace(" ", ""); + allowedModifiers |= DeclarationModifiers.ReadOnly; } - } - public override Symbol ContainingSymbol - { - get + allowedModifiers |= DeclarationModifiers.Extern; + + var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + + this.CheckUnsafeModifier(mods, diagnostics); + + ModifierUtils.ReportDefaultInterfaceImplementationModifiers(accessorsHaveImplementation, mods, + defaultInterfaceImplementationModifiers, + location, diagnostics); + + // Let's overwrite modifiers for interface properties with what they are supposed to be. + // Proper errors must have been reported by now. + if (isInterface) { - return _containingType; + mods = ModifierUtils.AdjustModifiersForAnInterfaceMember(mods, accessorsHaveImplementation, isExplicitInterfaceImplementation); } - } - public override NamedTypeSymbol ContainingType - { - get + if (isIndexer) { - return _containingType; - } - } - - internal override LexicalSortKey GetLexicalSortKey() - { - return new LexicalSortKey(Location, this.DeclaringCompilation); - } - - public override ImmutableArray Locations - { - get - { - return ImmutableArray.Create(Location); - } - } - - public override ImmutableArray DeclaringSyntaxReferences - { - get - { - return ImmutableArray.Create(_syntaxRef); - } - } - - public override bool IsAbstract - { - get { return (_modifiers & DeclarationModifiers.Abstract) != 0; } - } - - public override bool IsExtern - { - get { return (_modifiers & DeclarationModifiers.Extern) != 0; } - } - - public override bool IsStatic - { - get { return (_modifiers & DeclarationModifiers.Static) != 0; } - } - - internal bool IsFixed - { - get { return false; } - } - - /// - /// Even though it is declared with an IndexerDeclarationSyntax, an explicit - /// interface implementation is not an indexer because it will not cause the - /// containing type to be emitted with a DefaultMemberAttribute (and even if - /// there is another indexer, the name of the explicit implementation won't - /// match). This is important for round-tripping. - /// - public override bool IsIndexer - { - get { return (_modifiers & DeclarationModifiers.Indexer) != 0; } - } - - public override bool IsOverride - { - get { return (_modifiers & DeclarationModifiers.Override) != 0; } - } - - public override bool IsSealed - { - get { return (_modifiers & DeclarationModifiers.Sealed) != 0; } - } - - public override bool IsVirtual - { - get { return (_modifiers & DeclarationModifiers.Virtual) != 0; } - } - - internal bool IsNew - { - get { return (_modifiers & DeclarationModifiers.New) != 0; } - } - - internal bool HasReadOnlyModifier => (_modifiers & DeclarationModifiers.ReadOnly) != 0; - - public override MethodSymbol GetMethod - { - get { return _getMethod; } - } - - public override MethodSymbol SetMethod - { - get { return _setMethod; } - } - - internal override Microsoft.Cci.CallingConvention CallingConvention - { - get { return (IsStatic ? 0 : Microsoft.Cci.CallingConvention.HasThis); } - } - - public override ImmutableArray Parameters - { - get - { - if (_lazyParameters.IsDefault) - { - var diagnostics = DiagnosticBag.GetInstance(); - var binder = this.CreateBinderForTypeAndParameters(); - var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); - var result = this.ComputeParameters(binder, syntax, diagnostics); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyParameters, result)) - { - this.AddDeclarationDiagnostics(diagnostics); - } - diagnostics.Free(); - } - - return _lazyParameters; - } - } - - internal override bool IsExplicitInterfaceImplementation - => (_propertyFlags & Flags.IsExplicitInterfaceImplementation) != 0; - - public override ImmutableArray ExplicitInterfaceImplementations - { - get { return _explicitInterfaceImplementations; } - } - - public override ImmutableArray RefCustomModifiers - { - get { return _refCustomModifiers; } - } - - public override Accessibility DeclaredAccessibility - { - get - { - return ModifierUtils.EffectiveAccessibility(_modifiers); - } - } - - public bool HasSkipLocalsInitAttribute - { - get - { - var data = this.GetDecodedWellKnownAttributeData(); - return data?.HasSkipLocalsInitAttribute == true; - } - } - - internal override bool IsAutoProperty - => (_propertyFlags & Flags.IsAutoProperty) != 0; - - /// - /// Backing field for automatically implemented property, or - /// for a property with an initializer. - /// - internal override SynthesizedBackingFieldSymbol BackingField { get; } - - internal override bool MustCallMethodsDirectly - { - get { return false; } - } - - internal SyntaxReference SyntaxReference - { - get - { - return _syntaxRef; + mods |= DeclarationModifiers.Indexer; } - } - internal BasePropertyDeclarationSyntax CSharpSyntaxNode - { - get - { - return (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); - } + return mods; } - public override SyntaxList AttributeDeclarationSyntaxList - => CSharpSyntaxNode.AttributeLists; - - internal SyntaxTree SyntaxTree + protected override SourcePropertyAccessorSymbol? CreateAccessorSymbol( + bool isGet, + CSharpSyntaxNode? syntaxOpt, + PropertySymbol? explicitlyImplementedPropertyOpt, + string aliasQualifierOpt, + bool isAutoPropertyAccessor, + bool isExplicitInterfaceImplementation, + DiagnosticBag diagnostics) { - get + if (syntaxOpt is null) { - return _syntaxRef.SyntaxTree; + return null; } - } - - internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) + return SourcePropertyAccessorSymbol.CreateAccessorSymbol( + ContainingType, + this, + _modifiers, + _sourceName, + (AccessorDeclarationSyntax)syntaxOpt, + explicitlyImplementedPropertyOpt, + aliasQualifierOpt, + isAutoPropertyAccessor, + isExplicitInterfaceImplementation, + diagnostics); + } + + protected override SourcePropertyAccessorSymbol CreateExpressionBodiedAccessor( + ArrowExpressionClauseSyntax syntax, + PropertySymbol? explicitlyImplementedPropertyOpt, + string aliasQualifierOpt, + bool isExplicitInterfaceImplementation, + DiagnosticBag diagnostics) { - Location location = CSharpSyntaxNode.Type.Location; - var compilation = DeclaringCompilation; - - Debug.Assert(location != null); - - // Check constraints on return type and parameters. Note: Dev10 uses the - // property name location for any such errors. We'll do the same for return - // type errors but for parameter errors, we'll use the parameter location. - - if ((object)_explicitInterfaceType != null) - { - var explicitInterfaceSpecifier = GetExplicitInterfaceSpecifier(this.CSharpSyntaxNode); - Debug.Assert(explicitInterfaceSpecifier != null); - _explicitInterfaceType.CheckAllConstraints(compilation, conversions, new SourceLocation(explicitInterfaceSpecifier.Name), diagnostics); - - // Note: we delayed nullable-related checks that could pull on NonNullTypes - if (!_explicitInterfaceImplementations.IsEmpty) - { - TypeSymbol.CheckNullableReferenceTypeMismatchOnImplementingMember(this.ContainingType, this, _explicitInterfaceImplementations[0], isExplicit: true, diagnostics); - } - } - - if (_refKind == RefKind.RefReadOnly) - { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (Type.ContainsNativeInteger()) - { - compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (compilation.ShouldEmitNullableAttributes(this) && - this.TypeWithAnnotations.NeedsNullableAttribute()) - { - compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + return SourcePropertyAccessorSymbol.CreateAccessorSymbol( + ContainingType, + this, + _modifiers, + _sourceName, + syntax, + explicitlyImplementedPropertyOpt, + aliasQualifierOpt, + isExplicitInterfaceImplementation, + diagnostics); } - private void CheckAccessibility(Location location, DiagnosticBag diagnostics, bool isExplicitInterfaceImplementation) + private Binder CreateBinderForTypeAndParameters() { - var info = ModifierUtils.CheckAccessibility(_modifiers, this, isExplicitInterfaceImplementation); - if (info != null) - { - diagnostics.Add(new CSDiagnostic(info, location)); - } + var compilation = this.DeclaringCompilation; + var syntaxTree = SyntaxTree; + var syntax = CSharpSyntaxNode; + var binderFactory = compilation.GetBinderFactory(syntaxTree); + var binder = binderFactory.GetBinder(syntax, syntax, this); + SyntaxTokenList modifiers = GetModifierTokens(syntax); + binder = binder.WithUnsafeRegionIfNecessary(modifiers); + return binder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); } - private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool isExplicitInterfaceImplementation, - bool isIndexer, bool accessorsHaveImplementation, - Location location, DiagnosticBag diagnostics, out bool modifierErrors) + protected override TypeWithAnnotations ComputeType(Binder? binder, SyntaxNode syntax, DiagnosticBag diagnostics) { - bool isInterface = this.ContainingType.IsInterface; - var defaultAccess = isInterface && !isExplicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; - - // Check that the set of modifiers is allowed - var allowedModifiers = DeclarationModifiers.Unsafe; - var defaultInterfaceImplementationModifiers = DeclarationModifiers.None; - - if (!isExplicitInterfaceImplementation) - { - allowedModifiers |= DeclarationModifiers.New | - DeclarationModifiers.Sealed | - DeclarationModifiers.Abstract | - DeclarationModifiers.Virtual | - DeclarationModifiers.AccessibilityMask; - - if (!isIndexer) - { - allowedModifiers |= DeclarationModifiers.Static; - } - - if (!isInterface) - { - allowedModifiers |= DeclarationModifiers.Override; - } - else - { - // This is needed to make sure we can detect 'public' modifier specified explicitly and - // check it against language version below. - defaultAccess = DeclarationModifiers.None; + binder ??= CreateBinderForTypeAndParameters(); - defaultInterfaceImplementationModifiers |= DeclarationModifiers.Sealed | - DeclarationModifiers.Abstract | - (isIndexer ? 0 : DeclarationModifiers.Static) | - DeclarationModifiers.Virtual | - DeclarationModifiers.Extern | - DeclarationModifiers.AccessibilityMask; - } - } - else if (isInterface) - { - Debug.Assert(isExplicitInterfaceImplementation); - allowedModifiers |= DeclarationModifiers.Abstract; - } + RefKind refKind; + var typeSyntax = GetTypeSyntax(syntax).SkipRef(out refKind); + var type = binder.BindType(typeSyntax, diagnostics); + HashSet? useSiteDiagnostics = null; - if (ContainingType.IsStructType()) + if (GetExplicitInterfaceSpecifier(syntax) is null && !this.IsNoMoreVisibleThan(type, ref useSiteDiagnostics)) { - allowedModifiers |= DeclarationModifiers.ReadOnly; + // "Inconsistent accessibility: indexer return type '{1}' is less accessible than indexer '{0}'" + // "Inconsistent accessibility: property type '{1}' is less accessible than property '{0}'" + diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), Location, this, type.Type); } - allowedModifiers |= DeclarationModifiers.Extern; - - var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); - - this.CheckUnsafeModifier(mods, diagnostics); - - ModifierUtils.ReportDefaultInterfaceImplementationModifiers(accessorsHaveImplementation, mods, - defaultInterfaceImplementationModifiers, - location, diagnostics); - - // Let's overwrite modifiers for interface properties with what they are supposed to be. - // Proper errors must have been reported by now. - if (isInterface) - { - mods = ModifierUtils.AdjustModifiersForAnInterfaceMember(mods, accessorsHaveImplementation, isExplicitInterfaceImplementation); - } + diagnostics.Add(Location, useSiteDiagnostics); - if (isIndexer) + if (type.IsVoidType()) { - mods |= DeclarationModifiers.Indexer; + ErrorCode errorCode = this.IsIndexer ? ErrorCode.ERR_IndexerCantHaveVoidType : ErrorCode.ERR_PropertyCantHaveVoidType; + diagnostics.Add(errorCode, Location, this); } - return mods; + return type; } private static ImmutableArray MakeParameters( - Binder binder, SourcePropertySymbol owner, BaseParameterListSyntax parameterSyntaxOpt, DiagnosticBag diagnostics, bool addRefReadOnlyModifier) + Binder binder, SourcePropertySymbolBase owner, BaseParameterListSyntax? parameterSyntaxOpt, DiagnosticBag diagnostics, bool addRefReadOnlyModifier) { if (parameterSyntaxOpt == null) { @@ -981,728 +356,43 @@ private void CheckAccessibility(Location location, DiagnosticBag diagnostics, bo return parameters; } - private void CheckModifiers(bool isExplicitInterfaceImplementation, Location location, bool isIndexer, DiagnosticBag diagnostics) + protected override ImmutableArray ComputeParameters(Binder? binder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { - bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface; + binder ??= CreateBinderForTypeAndParameters(); - if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride)) - { - diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); - } - else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) - { - // A static member '{0}' cannot be marked as override, virtual, or abstract - diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); - } - else if (IsStatic && HasReadOnlyModifier) - { - // Static member '{0}' cannot be marked 'readonly'. - diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); - } - else if (IsOverride && (IsNew || IsVirtual)) - { - // A member '{0}' marked as override cannot be marked as new or virtual - diagnostics.Add(ErrorCode.ERR_OverrideNotNew, location, this); - } - else if (IsSealed && !IsOverride && !(IsAbstract && isExplicitInterfaceImplementationInInterface)) - { - // '{0}' cannot be sealed because it is not an override - diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this); - } - else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct) - { - // The modifier '{0}' is not valid for this item - diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.AbstractKeyword)); - } - else if (IsVirtual && ContainingType.TypeKind == TypeKind.Struct) - { - // The modifier '{0}' is not valid for this item - diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.VirtualKeyword)); - } - else if (IsAbstract && IsExtern) - { - diagnostics.Add(ErrorCode.ERR_AbstractAndExtern, location, this); - } - else if (IsAbstract && IsSealed && !isExplicitInterfaceImplementationInInterface) - { - diagnostics.Add(ErrorCode.ERR_AbstractAndSealed, location, this); - } - else if (IsAbstract && IsVirtual) - { - diagnostics.Add(ErrorCode.ERR_AbstractNotVirtual, location, this.Kind.Localize(), this); - } - else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride) - { - diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this); - } - else if (ContainingType.IsStatic && !IsStatic) - { - ErrorCode errorCode = isIndexer ? ErrorCode.ERR_IndexerInStaticClass : ErrorCode.ERR_InstanceMemberInStaticClass; - diagnostics.Add(errorCode, location, this); - } - } + var parameterSyntaxOpt = GetParameterListSyntax(syntax); + var parameters = MakeParameters(binder, this, parameterSyntaxOpt, diagnostics, addRefReadOnlyModifier: IsVirtual || IsAbstract); + HashSet? useSiteDiagnostics = null; - // Create AccessorSymbol for AccessorDeclarationSyntax - private SourcePropertyAccessorSymbol CreateAccessorSymbol(AccessorDeclarationSyntax syntaxOpt, - PropertySymbol explicitlyImplementedPropertyOpt, string aliasQualifierOpt, bool isAutoPropertyAccessor, bool isExplicitInterfaceImplementation, DiagnosticBag diagnostics) - { - if (syntaxOpt == null) + foreach (ParameterSymbol param in parameters) { - return null; + if (GetExplicitInterfaceSpecifier(syntax) == null && !this.IsNoMoreVisibleThan(param.Type, ref useSiteDiagnostics)) + { + diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, param.Type); + } + else if (SetMethod is object && param.Name == ParameterSymbol.ValueParameterName) + { + diagnostics.Add(ErrorCode.ERR_DuplicateGeneratedName, param.Locations.FirstOrDefault() ?? Location, param.Name); + } } - return SourcePropertyAccessorSymbol.CreateAccessorSymbol(_containingType, this, _modifiers, _sourceName, syntaxOpt, - explicitlyImplementedPropertyOpt, aliasQualifierOpt, isAutoPropertyAccessor, isExplicitInterfaceImplementation, diagnostics); - } - private void CheckAccessibilityMoreRestrictive(SourcePropertyAccessorSymbol accessor, DiagnosticBag diagnostics) - { - if (((object)accessor != null) && - !IsAccessibilityMoreRestrictive(this.DeclaredAccessibility, accessor.LocalAccessibility)) - { - diagnostics.Add(ErrorCode.ERR_InvalidPropertyAccessMod, accessor.Locations[0], accessor, this); - } + diagnostics.Add(Location, useSiteDiagnostics); + return parameters; } - /// - /// Return true if the accessor accessibility is more restrictive - /// than the property accessibility, otherwise false. - /// - private static bool IsAccessibilityMoreRestrictive(Accessibility property, Accessibility accessor) + protected override bool HasPointerTypeSyntactically { - if (accessor == Accessibility.NotApplicable) + get { - return true; + var typeSyntax = GetTypeSyntax(CSharpSyntaxNode).SkipRef(out _); + return typeSyntax.Kind() switch { SyntaxKind.PointerType => true, SyntaxKind.FunctionPointerType => true, _ => false }; } - return (accessor < property) && - ((accessor != Accessibility.Protected) || (property != Accessibility.Internal)); } - private static void CheckAbstractPropertyAccessorNotPrivate(SourcePropertyAccessorSymbol accessor, DiagnosticBag diagnostics) - { - if (accessor.LocalAccessibility == Accessibility.Private) - { - diagnostics.Add(ErrorCode.ERR_PrivateAbstractAccessor, accessor.Locations[0], accessor); - } - } + protected override ExplicitInterfaceSpecifierSyntax? GetExplicitInterfaceSpecifier(SyntaxNode syntax) + => ((BasePropertyDeclarationSyntax)syntax).ExplicitInterfaceSpecifier; - public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) - { - ref var lazyDocComment = ref expandIncludes ? ref _lazyExpandedDocComment : ref _lazyDocComment; - return SourceDocumentationCommentUtils.GetAndCacheDocumentationComment(this, expandIncludes, ref lazyDocComment); - } - - // Separate these checks out of FindExplicitlyImplementedProperty because they depend on the accessor symbols, - // which depend on the explicitly implemented property - private void CheckExplicitImplementationAccessor(MethodSymbol thisAccessor, MethodSymbol otherAccessor, PropertySymbol explicitlyImplementedProperty, DiagnosticBag diagnostics) - { - var thisHasAccessor = (object)thisAccessor != null; - var otherHasAccessor = otherAccessor.IsImplementable(); - - if (otherHasAccessor && !thisHasAccessor) - { - diagnostics.Add(ErrorCode.ERR_ExplicitPropertyMissingAccessor, this.Location, this, otherAccessor); - } - else if (!otherHasAccessor && thisHasAccessor) - { - diagnostics.Add(ErrorCode.ERR_ExplicitPropertyAddingAccessor, thisAccessor.Locations[0], thisAccessor, explicitlyImplementedProperty); - } - else if (TypeSymbol.HaveInitOnlyMismatch(thisAccessor, otherAccessor)) - { - Debug.Assert(thisAccessor.MethodKind == MethodKind.PropertySet); - diagnostics.Add(ErrorCode.ERR_ExplicitPropertyMismatchInitOnly, thisAccessor.Locations[0], thisAccessor, otherAccessor); - } - } - - internal override OverriddenOrHiddenMembersResult OverriddenOrHiddenMembers - { - get - { - if (_lazyOverriddenOrHiddenMembers == null) - { - Interlocked.CompareExchange(ref _lazyOverriddenOrHiddenMembers, this.MakeOverriddenOrHiddenMembers(), null); - } - return _lazyOverriddenOrHiddenMembers; - } - } - - /// - /// If this property is sealed, then we have to emit both accessors - regardless of whether - /// they are present in the source - so that they can be marked final. (i.e. sealed). - /// - internal SynthesizedSealedPropertyAccessor SynthesizedSealedAccessorOpt - { - get - { - bool hasGetter = (object)_getMethod != null; - bool hasSetter = (object)_setMethod != null; - if (!this.IsSealed || (hasGetter && hasSetter)) - { - return null; - } - - // This has to be cached because the CCI layer depends on reference equality. - // However, there's no point in having more than one field, since we don't - // expect to have to synthesize more than one accessor. - if ((object)_lazySynthesizedSealedAccessor == null) - { - Interlocked.CompareExchange(ref _lazySynthesizedSealedAccessor, MakeSynthesizedSealedAccessor(), null); - } - return _lazySynthesizedSealedAccessor; - } - } - - /// - /// Only non-null for sealed properties without both accessors. - /// - private SynthesizedSealedPropertyAccessor MakeSynthesizedSealedAccessor() - { - Debug.Assert(this.IsSealed && ((object)_getMethod == null || (object)_setMethod == null)); - - if ((object)_getMethod != null) - { - // need to synthesize setter - MethodSymbol overriddenAccessor = this.GetOwnOrInheritedSetMethod(); - return (object)overriddenAccessor == null ? null : new SynthesizedSealedPropertyAccessor(this, overriddenAccessor); - } - else if ((object)_setMethod != null) - { - // need to synthesize getter - MethodSymbol overriddenAccessor = this.GetOwnOrInheritedGetMethod(); - return (object)overriddenAccessor == null ? null : new SynthesizedSealedPropertyAccessor(this, overriddenAccessor); - } - else - { - // Arguably, it would be more correct to return an array containing two - // synthesized accessors, but we're already in an error case, so we'll - // minimize the cascading error behavior by suppressing synthesis. - return null; - } - } - - #region Attributes - - protected override IAttributeTargetSymbol AttributesOwner => this; - - protected override AttributeLocation DefaultAttributeLocation => AttributeLocation.Property; - - protected override AttributeLocation AllowedAttributeLocations - => (_propertyFlags & Flags.IsAutoProperty) != 0 - ? AttributeLocation.Property | AttributeLocation.Field - : AttributeLocation.Property; - - /// - /// Returns a bag of custom attributes applied on the property and data decoded from well-known attributes. Returns null if there are no attributes. - /// - /// - /// Forces binding and decoding of attributes. - /// - private CustomAttributesBag GetAttributesBag() - { - var bag = _lazyCustomAttributesBag; - if (bag != null && bag.IsSealed) - { - return bag; - } - - // The property is responsible for completion of the backing field - _ = BackingField?.GetAttributes(); - - if (LoadAndValidateAttributes(OneOrMany.Create(this.CSharpSyntaxNode.AttributeLists), ref _lazyCustomAttributesBag)) - { - var completed = _state.NotePartComplete(CompletionPart.Attributes); - Debug.Assert(completed); - } - - Debug.Assert(_lazyCustomAttributesBag.IsSealed); - return _lazyCustomAttributesBag; - } - - /// - /// Gets the attributes applied on this symbol. - /// Returns an empty array if there are no attributes. - /// - /// - /// NOTE: This method should always be kept as a sealed override. - /// If you want to override attribute binding logic for a sub-class, then override method. - /// - public sealed override ImmutableArray GetAttributes() - { - return this.GetAttributesBag().Attributes; - } - - /// - /// Returns data decoded from well-known attributes applied to the symbol or null if there are no applied attributes. - /// - /// - /// Forces binding and decoding of attributes. - /// - private PropertyWellKnownAttributeData GetDecodedWellKnownAttributeData() - { - var attributesBag = _lazyCustomAttributesBag; - if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed) - { - attributesBag = this.GetAttributesBag(); - } - - return (PropertyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData; - } - - /// - /// Returns data decoded from special early bound well-known attributes applied to the symbol or null if there are no applied attributes. - /// - /// - /// Forces binding and decoding of attributes. - /// - internal PropertyEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData() - { - var attributesBag = _lazyCustomAttributesBag; - if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) - { - attributesBag = this.GetAttributesBag(); - } - - return (PropertyEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData; - } - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - var compilation = this.DeclaringCompilation; - var type = this.TypeWithAnnotations; - - if (type.Type.ContainsDynamic()) - { - AddSynthesizedAttribute(ref attributes, - compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length + RefCustomModifiers.Length, _refKind)); - } - - if (type.Type.ContainsNativeInteger()) - { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNativeIntegerAttribute(this, type.Type)); - } - - if (type.Type.ContainsTupleNames()) - { - AddSynthesizedAttribute(ref attributes, - compilation.SynthesizeTupleNamesAttribute(type.Type)); - } - - if (compilation.ShouldEmitNullableAttributes(this)) - { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, ContainingType.GetNullableContextValue(), type)); - } - - if (this.ReturnsByRefReadonly) - { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); - } - } - - internal sealed override bool IsDirectlyExcludedFromCodeCoverage => - GetDecodedWellKnownAttributeData()?.HasExcludeFromCodeCoverageAttribute == true; - - internal override bool HasSpecialName - { - get - { - var data = GetDecodedWellKnownAttributeData(); - return data != null && data.HasSpecialNameAttribute; - } - } - - internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) - { - CSharpAttributeData boundAttribute; - ObsoleteAttributeData obsoleteData; - - if (EarlyDecodeDeprecatedOrExperimentalOrObsoleteAttribute(ref arguments, out boundAttribute, out obsoleteData)) - { - if (obsoleteData != null) - { - arguments.GetOrCreateData().ObsoleteAttributeData = obsoleteData; - } - - return boundAttribute; - } - - if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.IndexerNameAttribute)) - { - bool hasAnyDiagnostics; - boundAttribute = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); - if (!boundAttribute.HasErrors) - { - string indexerName = boundAttribute.CommonConstructorArguments[0].DecodeValue(SpecialType.System_String); - if (indexerName != null) - { - arguments.GetOrCreateData().IndexerName = indexerName; - } - - if (!hasAnyDiagnostics) - { - return boundAttribute; - } - } - - return null; - } - - return base.EarlyDecodeWellKnownAttribute(ref arguments); - } - - /// - /// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute. - /// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet. - /// - internal override ObsoleteAttributeData ObsoleteAttributeData - { - get - { - if (!_containingType.AnyMemberHasAttributes) - { - return null; - } - - var lazyCustomAttributesBag = _lazyCustomAttributesBag; - if (lazyCustomAttributesBag != null && lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) - { - return ((PropertyEarlyWellKnownAttributeData)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData)?.ObsoleteAttributeData; - } - - return ObsoleteAttributeData.Uninitialized; - } - } - - internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments arguments) - { - Debug.Assert(arguments.AttributeSyntaxOpt != null); - - var attribute = arguments.Attribute; - Debug.Assert(!attribute.HasErrors); - Debug.Assert(arguments.SymbolPart == AttributeLocation.None); - - if (attribute.IsTargetAttribute(this, AttributeDescription.IndexerNameAttribute)) - { - //NOTE: decoding was done by EarlyDecodeWellKnownAttribute. - ValidateIndexerNameAttribute(attribute, arguments.AttributeSyntaxOpt, arguments.Diagnostics); - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.SpecialNameAttribute)) - { - arguments.GetOrCreateData().HasSpecialNameAttribute = true; - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.ExcludeFromCodeCoverageAttribute)) - { - arguments.GetOrCreateData().HasExcludeFromCodeCoverageAttribute = true; - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.SkipLocalsInitAttribute)) - { - CSharpAttributeData.DecodeSkipLocalsInitAttribute(DeclaringCompilation, ref arguments); - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.DynamicAttribute)) - { - // DynamicAttribute should not be set explicitly. - arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitDynamicAttr, arguments.AttributeSyntaxOpt.Location); - } - else if (ReportExplicitUseOfReservedAttributes(in arguments, - ReservedAttributes.DynamicAttribute | ReservedAttributes.IsReadOnlyAttribute | ReservedAttributes.IsUnmanagedAttribute | ReservedAttributes.IsByRefLikeAttribute | ReservedAttributes.TupleElementNamesAttribute | ReservedAttributes.NullableAttribute | ReservedAttributes.NativeIntegerAttribute)) - { - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.DisallowNullAttribute)) - { - arguments.GetOrCreateData().HasDisallowNullAttribute = true; - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.AllowNullAttribute)) - { - arguments.GetOrCreateData().HasAllowNullAttribute = true; - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.MaybeNullAttribute)) - { - arguments.GetOrCreateData().HasMaybeNullAttribute = true; - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.NotNullAttribute)) - { - arguments.GetOrCreateData().HasNotNullAttribute = true; - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.MemberNotNullAttribute)) - { - MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); - CSharpAttributeData.DecodeMemberNotNullAttribute(ContainingType, ref arguments); - } - else if (attribute.IsTargetAttribute(this, AttributeDescription.MemberNotNullWhenAttribute)) - { - MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); - CSharpAttributeData.DecodeMemberNotNullWhenAttribute(ContainingType, ref arguments); - } - } - - internal bool HasDisallowNull - { - get - { - var data = GetDecodedWellKnownAttributeData(); - return data != null && data.HasDisallowNullAttribute; - } - } - - internal bool HasAllowNull - { - get - { - var data = GetDecodedWellKnownAttributeData(); - return data != null && data.HasAllowNullAttribute; - } - } - - internal bool HasMaybeNull - { - get - { - var data = GetDecodedWellKnownAttributeData(); - return data != null && data.HasMaybeNullAttribute; - } - } - - internal bool HasNotNull - { - get - { - var data = GetDecodedWellKnownAttributeData(); - return data != null && data.HasNotNullAttribute; - } - } - - internal SourceAttributeData DisallowNullAttributeIfExists - => FindAttribute(AttributeDescription.DisallowNullAttribute); - - internal SourceAttributeData AllowNullAttributeIfExists - => FindAttribute(AttributeDescription.AllowNullAttribute); - - internal SourceAttributeData MaybeNullAttributeIfExists - => FindAttribute(AttributeDescription.MaybeNullAttribute); - - internal SourceAttributeData NotNullAttributeIfExists - => FindAttribute(AttributeDescription.NotNullAttribute); - - internal ImmutableArray MemberNotNullAttributeIfExists - => FindAttributes(AttributeDescription.MemberNotNullAttribute); - - internal ImmutableArray MemberNotNullWhenAttributeIfExists - => FindAttributes(AttributeDescription.MemberNotNullWhenAttribute); - - private SourceAttributeData FindAttribute(AttributeDescription attributeDescription) - => (SourceAttributeData)GetAttributes().First(a => a.IsTargetAttribute(this, attributeDescription)); - - private ImmutableArray FindAttributes(AttributeDescription attributeDescription) - => GetAttributes().Where(a => a.IsTargetAttribute(this, attributeDescription)).Cast().ToImmutableArray(); - - internal override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) - { - Debug.Assert(!boundAttributes.IsDefault); - Debug.Assert(!allAttributeSyntaxNodes.IsDefault); - Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length); - Debug.Assert(_lazyCustomAttributesBag != null); - Debug.Assert(_lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed); - Debug.Assert(symbolPart == AttributeLocation.None); - - base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); - } - - private void ValidateIndexerNameAttribute(CSharpAttributeData attribute, AttributeSyntax node, DiagnosticBag diagnostics) - { - if (!this.IsIndexer || this.IsExplicitInterfaceImplementation) - { - diagnostics.Add(ErrorCode.ERR_BadIndexerNameAttr, node.Name.Location, node.GetErrorDisplayName()); - } - else - { - string indexerName = attribute.CommonConstructorArguments[0].DecodeValue(SpecialType.System_String); - if (indexerName == null || !SyntaxFacts.IsValidIdentifier(indexerName)) - { - diagnostics.Add(ErrorCode.ERR_BadArgumentToAttribute, node.ArgumentList.Arguments[0].Location, node.GetErrorDisplayName()); - } - } - } - - #endregion - - #region Completion - - internal sealed override bool RequiresCompletion - { - get { return true; } - } - - internal sealed override bool HasComplete(CompletionPart part) - { - return _state.HasComplete(part); - } - - internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken) - { - while (true) - { - cancellationToken.ThrowIfCancellationRequested(); - var incompletePart = _state.NextIncompletePart; - switch (incompletePart) - { - case CompletionPart.Attributes: - GetAttributes(); - break; - - case CompletionPart.StartPropertyParameters: - case CompletionPart.FinishPropertyParameters: - { - if (_state.NotePartComplete(CompletionPart.StartPropertyParameters)) - { - var parameters = this.Parameters; - if (parameters.Length > 0) - { - var diagnostics = DiagnosticBag.GetInstance(); - var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary); - foreach (var parameter in this.Parameters) - { - parameter.ForceComplete(locationOpt, cancellationToken); - parameter.Type.CheckAllConstraints(DeclaringCompilation, conversions, parameter.Locations[0], diagnostics); - } - - this.AddDeclarationDiagnostics(diagnostics); - diagnostics.Free(); - } - - DeclaringCompilation.SymbolDeclaredEvent(this); - var completedOnThisThread = _state.NotePartComplete(CompletionPart.FinishPropertyParameters); - Debug.Assert(completedOnThisThread); - } - else - { - // StartPropertyParameters was completed by another thread. Wait for it to finish the parameters. - _state.SpinWaitComplete(CompletionPart.FinishPropertyParameters, cancellationToken); - } - } - break; - - case CompletionPart.StartPropertyType: - case CompletionPart.FinishPropertyType: - { - if (_state.NotePartComplete(CompletionPart.StartPropertyType)) - { - var diagnostics = DiagnosticBag.GetInstance(); - var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary); - this.Type.CheckAllConstraints(DeclaringCompilation, conversions, Location, diagnostics); - - var type = this.Type; - if (type.IsRestrictedType(ignoreSpanLikeTypes: true)) - { - diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, this.CSharpSyntaxNode.Type.Location, type); - } - else if (this.IsAutoProperty && type.IsRefLikeType && (this.IsStatic || !this.ContainingType.IsRefLikeType)) - { - diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, this.CSharpSyntaxNode.Type.Location, type); - } - - this.AddDeclarationDiagnostics(diagnostics); - var completedOnThisThread = _state.NotePartComplete(CompletionPart.FinishPropertyType); - Debug.Assert(completedOnThisThread); - diagnostics.Free(); - } - else - { - // StartPropertyType was completed by another thread. Wait for it to finish the type. - _state.SpinWaitComplete(CompletionPart.FinishPropertyType, cancellationToken); - } - } - break; - - case CompletionPart.None: - return; - - default: - // any other values are completion parts intended for other kinds of symbols - _state.NotePartComplete(CompletionPart.All & ~CompletionPart.PropertySymbolAll); - break; - } - - _state.SpinWaitComplete(incompletePart, cancellationToken); - } - } - - #endregion - - private TypeWithAnnotations ComputeType(Binder binder, BasePropertyDeclarationSyntax syntax, DiagnosticBag diagnostics) - { - RefKind refKind; - var typeSyntax = syntax.Type.SkipRef(out refKind); - var type = binder.BindType(typeSyntax, diagnostics); - HashSet useSiteDiagnostics = null; - - if (syntax.ExplicitInterfaceSpecifier == null && !this.IsNoMoreVisibleThan(type, ref useSiteDiagnostics)) - { - // "Inconsistent accessibility: indexer return type '{1}' is less accessible than indexer '{0}'" - // "Inconsistent accessibility: property type '{1}' is less accessible than property '{0}'" - diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), Location, this, type.Type); - } - - diagnostics.Add(Location, useSiteDiagnostics); - - if (type.IsVoidType()) - { - ErrorCode errorCode = this.IsIndexer ? ErrorCode.ERR_IndexerCantHaveVoidType : ErrorCode.ERR_PropertyCantHaveVoidType; - diagnostics.Add(errorCode, Location, this); - } - - return type; - } - - private ImmutableArray ComputeParameters(Binder binder, BasePropertyDeclarationSyntax syntax, DiagnosticBag diagnostics) - { - var parameterSyntaxOpt = GetParameterListSyntax(syntax); - var parameters = MakeParameters(binder, this, parameterSyntaxOpt, diagnostics, addRefReadOnlyModifier: IsVirtual || IsAbstract); - HashSet useSiteDiagnostics = null; - - foreach (ParameterSymbol param in parameters) - { - if (syntax.ExplicitInterfaceSpecifier == null && !this.IsNoMoreVisibleThan(param.Type, ref useSiteDiagnostics)) - { - diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, param.Type); - } - else if ((object)_setMethod != null && param.Name == ParameterSymbol.ValueParameterName) - { - diagnostics.Add(ErrorCode.ERR_DuplicateGeneratedName, param.Locations.FirstOrDefault() ?? Location, param.Name); - } - } - - diagnostics.Add(Location, useSiteDiagnostics); - return parameters; - } - - private Binder CreateBinderForTypeAndParameters() - { - var compilation = this.DeclaringCompilation; - var syntaxTree = _syntaxRef.SyntaxTree; - var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); - var binderFactory = compilation.GetBinderFactory(syntaxTree); - var binder = binderFactory.GetBinder(syntax, syntax, this); - SyntaxTokenList modifiers = syntax.Modifiers; - binder = binder.WithUnsafeRegionIfNecessary(modifiers); - return binder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); - } - - private static ExplicitInterfaceSpecifierSyntax GetExplicitInterfaceSpecifier(BasePropertyDeclarationSyntax syntax) - { - switch (syntax.Kind()) - { - case SyntaxKind.PropertyDeclaration: - return ((PropertyDeclarationSyntax)syntax).ExplicitInterfaceSpecifier; - case SyntaxKind.IndexerDeclaration: - return ((IndexerDeclarationSyntax)syntax).ExplicitInterfaceSpecifier; - default: - throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); - } - } - - private static BaseParameterListSyntax GetParameterListSyntax(BasePropertyDeclarationSyntax syntax) - { - return (syntax.Kind() == SyntaxKind.IndexerDeclaration) ? ((IndexerDeclarationSyntax)syntax).ParameterList : null; - } + protected override BaseParameterListSyntax? GetParameterListSyntax(CSharpSyntaxNode syntax) + => (syntax as IndexerDeclarationSyntax)?.ParameterList; } -} +} \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs new file mode 100644 index 00000000000..0245143c7ac --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -0,0 +1,1470 @@ +๏ปฟ// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal abstract class SourcePropertySymbolBase : PropertySymbol, IAttributeTargetSymbol + { + /// + /// Condensed flags storing useful information about the + /// so that we do not have to go back to source to compute this data. + /// + [Flags] + private enum Flags : byte + { + IsExpressionBodied = 1 << 0, + IsAutoProperty = 1 << 1, + IsExplicitInterfaceImplementation = 1 << 2, + } + + // TODO (tomat): consider splitting into multiple subclasses/rare data. + + private readonly SourceMemberContainerTypeSymbol _containingType; + private readonly string _name; + private readonly SyntaxReference _syntaxRef; + protected readonly DeclarationModifiers _modifiers; + private readonly ImmutableArray _refCustomModifiers; + private readonly SourcePropertyAccessorSymbol _getMethod; + private readonly SourcePropertyAccessorSymbol _setMethod; + private readonly TypeSymbol _explicitInterfaceType; + private readonly ImmutableArray _explicitInterfaceImplementations; + private readonly Flags _propertyFlags; + private readonly RefKind _refKind; + + private SymbolCompletionState _state; + private ImmutableArray _lazyParameters; + private TypeWithAnnotations.Boxed _lazyType; + + /// + /// Set in constructor, might be changed while decoding . + /// + protected readonly string _sourceName; + + private string _lazyDocComment; + private string _lazyExpandedDocComment; + private OverriddenOrHiddenMembersResult _lazyOverriddenOrHiddenMembers; + private SynthesizedSealedPropertyAccessor _lazySynthesizedSealedAccessor; + private CustomAttributesBag _lazyCustomAttributesBag; + + // CONSIDER: if the parameters were computed lazily, ParameterCount could be overridden to fall back on the syntax (as in SourceMemberMethodSymbol). + + public Location Location { get; } + +#nullable enable + /// + /// The is passed for explicit interface implementations or + /// overrides to . If a binder is not required in this situation + /// the parameter can be null. + /// + protected SourcePropertySymbolBase( + SourceMemberContainerTypeSymbol containingType, + Binder? binder, + CSharpSyntaxNode syntax, + RefKind refKind, + string name, + Location location, + DiagnosticBag diagnostics) + { + _syntaxRef = syntax.GetReference(); + Location = location; + + // This has the value that IsIndexer will ultimately have, once we've populated the fields of this object. + bool isIndexer = syntax.Kind() == SyntaxKind.IndexerDeclaration; + var interfaceSpecifier = GetExplicitInterfaceSpecifier(syntax); + bool isExplicitInterfaceImplementation = interfaceSpecifier != null; + if (isExplicitInterfaceImplementation) + { + _propertyFlags |= Flags.IsExplicitInterfaceImplementation; + } + + _containingType = containingType; + _refKind = refKind; + + SyntaxTokenList modifiers = GetModifierTokens(syntax); + if (binder is object) + { + binder = binder.WithUnsafeRegionIfNecessary(modifiers); + binder = binder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); + } + + var arrowExpression = GetArrowExpression(syntax); + bool hasExpressionBody = arrowExpression != null; + bool hasInitializer = HasInitializer(syntax); + + GetAccessorDeclarations( + syntax, + diagnostics, + out bool isAutoProperty, + out bool hasAccessorList, + out bool accessorsHaveImplementation, + out bool isInitOnly, + out var getSyntax, + out var setSyntax); + + var hasGetAccessor = getSyntax is object; + var hasSetAccessor = setSyntax is object; + + bool modifierErrors; + _modifiers = MakeModifiers(modifiers, isExplicitInterfaceImplementation, isIndexer, + accessorsHaveImplementation, location, + diagnostics, out modifierErrors); + this.CheckAccessibility(location, diagnostics, isExplicitInterfaceImplementation); + + this.CheckModifiers(isExplicitInterfaceImplementation, location, isIndexer, diagnostics); + + isAutoProperty = isAutoProperty && (!(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer); + + if (isIndexer && !isExplicitInterfaceImplementation) + { + // Evaluate the attributes immediately in case the IndexerNameAttribute has been applied. + // NOTE: we want IsExplicitInterfaceImplementation, IsOverride, Locations, and the syntax reference + // to be initialized before we pass this symbol to LoadCustomAttributes. + + // CONSIDER: none of the information from this early binding pass is cached. Everything will + // be re-bound when someone calls GetAttributes. If this gets to be a problem, we could + // always use the real attribute bag of this symbol and modify LoadAndValidateAttributes to + // handle partially filled bags. + CustomAttributesBag? temp = null; + LoadAndValidateAttributes(OneOrMany.Create(AttributeDeclarationSyntaxList), ref temp, earlyDecodingOnly: true); + if (temp != null) + { + Debug.Assert(temp.IsEarlyDecodedWellKnownAttributeDataComputed); + var propertyData = (PropertyEarlyWellKnownAttributeData)temp.EarlyDecodedWellKnownAttributeData; + if (propertyData != null) + { + _sourceName = propertyData.IndexerName; + } + } + } + + string aliasQualifierOpt; + string memberName = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(binder, interfaceSpecifier, name, diagnostics, out _explicitInterfaceType, out aliasQualifierOpt); + _sourceName = _sourceName ?? memberName; //sourceName may have been set while loading attributes + _name = isIndexer ? ExplicitInterfaceHelpers.GetMemberName(WellKnownMemberNames.Indexer, _explicitInterfaceType, aliasQualifierOpt) : _sourceName; + + if (hasInitializer) + { + CheckInitializer(isAutoProperty, containingType.IsInterface, IsStatic, location, diagnostics); + } + + if (isAutoProperty || hasInitializer) + { + var isAutoPropertyWithGetSyntax = isAutoProperty && hasGetAccessor; + if (isAutoPropertyWithGetSyntax) + { + _propertyFlags |= Flags.IsAutoProperty; + } + + bool isGetterOnly = hasGetAccessor && !hasSetAccessor; + + if (isAutoPropertyWithGetSyntax && !IsStatic && !isGetterOnly) + { + if (ContainingType.IsReadOnly) + { + diagnostics.Add(ErrorCode.ERR_AutoPropsInRoStruct, location); + } + else if (HasReadOnlyModifier) + { + diagnostics.Add(ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly, location, this); + } + } + + if (isAutoPropertyWithGetSyntax || hasInitializer) + { + if (isAutoPropertyWithGetSyntax) + { + //issue a diagnostic if the compiler generated attribute ctor is not found. + Binder.ReportUseSiteDiagnosticForSynthesizedAttribute(DeclaringCompilation, + WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor, diagnostics, syntax: syntax); + + if (this._refKind != RefKind.None && !_containingType.IsInterface) + { + diagnostics.Add(ErrorCode.ERR_AutoPropertyCannotBeRefReturning, location, this); + } + } + + string fieldName = GeneratedNames.MakeBackingFieldName(_sourceName); + BackingField = new SynthesizedBackingFieldSymbol(this, + fieldName, + isReadOnly: isGetterOnly || isInitOnly, + this.IsStatic, + hasInitializer); + } + + if (isAutoProperty) + { + Binder.CheckFeatureAvailability( + syntax, + isGetterOnly ? MessageID.IDS_FeatureReadonlyAutoImplementedProperties : MessageID.IDS_FeatureAutoImplementedProperties, + diagnostics, + location); + } + } + + PropertySymbol? explicitlyImplementedProperty = null; + _refCustomModifiers = ImmutableArray.Empty; + + // The runtime will not treat the accessors of this property as overrides or implementations + // of those of another property unless both the signatures and the custom modifiers match. + // Hence, in the case of overrides and *explicit* implementations, we need to copy the custom + // modifiers that are in the signatures of the overridden/implemented property accessors. + // (From source, we know that there can only be one overridden/implemented property, so there + // are no conflicts.) This is unnecessary for implicit implementations because, if the custom + // modifiers don't match, we'll insert bridge methods for the accessors (explicit implementations + // that delegate to the implicit implementations) with the correct custom modifiers + // (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation). + + // Note: we're checking if the syntax indicates explicit implementation rather, + // than if explicitInterfaceType is null because we don't want to look for an + // overridden property if this is supposed to be an explicit implementation. + if (isExplicitInterfaceImplementation || this.IsOverride) + { + // Type and parameters for overrides and explicit implementations cannot be bound + // lazily since the property name depends on the metadata name of the base property, + // and the property name is required to add the property to the containing type, and + // the type and parameters are required to determine the override or implementation. + var type = this.ComputeType(binder, syntax, diagnostics); + _lazyType = new TypeWithAnnotations.Boxed(type); + _lazyParameters = this.ComputeParameters(binder, syntax, diagnostics); + + bool isOverride = false; + PropertySymbol? overriddenOrImplementedProperty = null; + + if (!isExplicitInterfaceImplementation) + { + // If this property is an override, we may need to copy custom modifiers from + // the overridden property (so that the runtime will recognize it as an override). + // We check for this case here, while we can still modify the parameters and + // return type without losing the appearance of immutability. + isOverride = true; + overriddenOrImplementedProperty = this.OverriddenProperty; + } + else + { + string interfacePropertyName = isIndexer ? WellKnownMemberNames.Indexer : name; + explicitlyImplementedProperty = this.FindExplicitlyImplementedProperty(_explicitInterfaceType, interfacePropertyName, interfaceSpecifier, diagnostics); + this.FindExplicitlyImplementedMemberVerification(explicitlyImplementedProperty, diagnostics); + overriddenOrImplementedProperty = explicitlyImplementedProperty; + } + + if ((object)overriddenOrImplementedProperty != null) + { + _refCustomModifiers = _refKind != RefKind.None ? overriddenOrImplementedProperty.RefCustomModifiers : ImmutableArray.Empty; + + TypeWithAnnotations overriddenPropertyType = overriddenOrImplementedProperty.TypeWithAnnotations; + + // We do an extra check before copying the type to handle the case where the overriding + // property (incorrectly) has a different type than the overridden property. In such cases, + // we want to retain the original (incorrect) type to avoid hiding the type given in source. + if (type.Type.Equals(overriddenPropertyType.Type, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamic)) + { + type = type.WithTypeAndModifiers( + CustomModifierUtils.CopyTypeCustomModifiers(overriddenPropertyType.Type, type.Type, this.ContainingAssembly), + overriddenPropertyType.CustomModifiers); + + _lazyType = new TypeWithAnnotations.Boxed(type); + } + + _lazyParameters = CustomModifierUtils.CopyParameterCustomModifiers(overriddenOrImplementedProperty.Parameters, _lazyParameters, alsoCopyParamsModifier: isOverride); + } + } + else if (_refKind == RefKind.RefReadOnly) + { + var modifierType = Binder.GetWellKnownType(DeclaringCompilation, WellKnownType.System_Runtime_InteropServices_InAttribute, diagnostics, TypeLocation); + + _refCustomModifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)); + } + + if (!hasAccessorList && hasExpressionBody) + { + Debug.Assert(arrowExpression is object); + _propertyFlags |= Flags.IsExpressionBodied; + _getMethod = CreateExpressionBodiedAccessor( + arrowExpression, + explicitlyImplementedProperty, + aliasQualifierOpt, + isExplicitInterfaceImplementation, + diagnostics); + _setMethod = null; + } + else + { + _getMethod = CreateAccessorSymbol(isGet: true, getSyntax, explicitlyImplementedProperty, aliasQualifierOpt, isAutoProperty, isExplicitInterfaceImplementation, diagnostics); + _setMethod = CreateAccessorSymbol(isGet: false, setSyntax, explicitlyImplementedProperty, aliasQualifierOpt, isAutoProperty, isExplicitInterfaceImplementation, diagnostics); + + if (!hasGetAccessor || !hasSetAccessor) + { + if (!hasGetAccessor && !hasSetAccessor) + { + diagnostics.Add(ErrorCode.ERR_PropertyWithNoAccessors, location, this); + } + else if (_refKind != RefKind.None) + { + if (!hasGetAccessor) + { + diagnostics.Add(ErrorCode.ERR_RefPropertyMustHaveGetAccessor, location, this); + } + } + else if (isAutoProperty) + { + var accessor = _getMethod ?? _setMethod; + if (!hasGetAccessor) + { + diagnostics.Add(ErrorCode.ERR_AutoPropertyMustHaveGetAccessor, accessor!.Locations[0], accessor); + } + } + } + + // Check accessor accessibility is more restrictive than property accessibility. + CheckAccessibilityMoreRestrictive(_getMethod, diagnostics); + CheckAccessibilityMoreRestrictive(_setMethod, diagnostics); + + if ((_getMethod is object) && (_setMethod is object)) + { + if (_refKind != RefKind.None) + { + diagnostics.Add(ErrorCode.ERR_RefPropertyCannotHaveSetAccessor, _setMethod.Locations[0], _setMethod); + } + else if ((_getMethod.LocalAccessibility != Accessibility.NotApplicable) && + (_setMethod.LocalAccessibility != Accessibility.NotApplicable)) + { + // Check accessibility is set on at most one accessor. + diagnostics.Add(ErrorCode.ERR_DuplicatePropertyAccessMods, location, this); + } + else if (_getMethod.LocalDeclaredReadOnly && _setMethod.LocalDeclaredReadOnly) + { + diagnostics.Add(ErrorCode.ERR_DuplicatePropertyReadOnlyMods, location, this); + } + else if (this.IsAbstract) + { + // Check abstract property accessors are not private. + CheckAbstractPropertyAccessorNotPrivate(_getMethod, diagnostics); + CheckAbstractPropertyAccessorNotPrivate(_setMethod, diagnostics); + } + } + else + { + if (!this.IsOverride) + { + var accessor = _getMethod ?? _setMethod; + if (accessor is object) + { + // Check accessibility is not set on the one accessor. + if (accessor.LocalAccessibility != Accessibility.NotApplicable) + { + diagnostics.Add(ErrorCode.ERR_AccessModMissingAccessor, location, this); + } + + // Check that 'readonly' is not set on the one accessor. + if (accessor.LocalDeclaredReadOnly) + { + diagnostics.Add(ErrorCode.ERR_ReadOnlyModMissingAccessor, location, this); + } + } + } + } + } + + if (explicitlyImplementedProperty is object) + { + CheckExplicitImplementationAccessor(this.GetMethod, explicitlyImplementedProperty.GetMethod, explicitlyImplementedProperty, diagnostics); + CheckExplicitImplementationAccessor(this.SetMethod, explicitlyImplementedProperty.SetMethod, explicitlyImplementedProperty, diagnostics); + } + + _explicitInterfaceImplementations = + explicitlyImplementedProperty is null ? + ImmutableArray.Empty : + ImmutableArray.Create(explicitlyImplementedProperty); + + // get-only auto property should not override settable properties + if ((_propertyFlags & Flags.IsAutoProperty) != 0) + { + if (_setMethod is null && !this.IsReadOnly) + { + diagnostics.Add(ErrorCode.ERR_AutoPropertyMustOverrideSet, location, this); + } + + CheckForFieldTargetedAttribute(diagnostics); + } + + CheckForBlockAndExpressionBody(syntax, diagnostics); + } + + protected abstract Location TypeLocation { get; } + + protected abstract SyntaxTokenList GetModifierTokens(SyntaxNode syntax); + + protected abstract ArrowExpressionClauseSyntax? GetArrowExpression(SyntaxNode syntax); + + protected abstract bool HasInitializer(SyntaxNode syntax); + + private void CheckForFieldTargetedAttribute(DiagnosticBag diagnostics) + { + var languageVersion = this.DeclaringCompilation.LanguageVersion; + if (languageVersion.AllowAttributesOnBackingFields()) + { + return; + } + + foreach (var attribute in AttributeDeclarationSyntaxList) + { + if (attribute.Target?.GetAttributeLocation() == AttributeLocation.Field) + { + diagnostics.Add( + new CSDiagnosticInfo(ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable, + languageVersion.ToDisplayString(), + new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureAttributesOnBackingFields.RequiredVersion())), + attribute.Target.Location); + } + } + } + + protected abstract void GetAccessorDeclarations( + CSharpSyntaxNode syntax, + DiagnosticBag diagnostics, + out bool isAutoProperty, + out bool hasAccessorList, + out bool accessorsHaveImplementation, + out bool isInitOnly, + out CSharpSyntaxNode? getSyntax, + out CSharpSyntaxNode? setSyntax); + + protected abstract void CheckForBlockAndExpressionBody(CSharpSyntaxNode syntax, DiagnosticBag diagnostics); + +#nullable restore + + internal sealed override ImmutableArray NotNullMembers => + GetDecodedWellKnownAttributeData()?.NotNullMembers ?? ImmutableArray.Empty; + + internal sealed override ImmutableArray NotNullWhenTrueMembers => + GetDecodedWellKnownAttributeData()?.NotNullWhenTrueMembers ?? ImmutableArray.Empty; + + internal sealed override ImmutableArray NotNullWhenFalseMembers => + GetDecodedWellKnownAttributeData()?.NotNullWhenFalseMembers ?? ImmutableArray.Empty; + + internal bool IsExpressionBodied + => (_propertyFlags & Flags.IsExpressionBodied) != 0; + + private void CheckInitializer( + bool isAutoProperty, + bool isInterface, + bool isStatic, + Location location, + DiagnosticBag diagnostics) + { + if (isInterface && !isStatic) + { + diagnostics.Add(ErrorCode.ERR_InstancePropertyInitializerInInterface, location, this); + } + else if (!isAutoProperty) + { + diagnostics.Add(ErrorCode.ERR_InitializerOnNonAutoProperty, location, this); + } + } + + public override RefKind RefKind + { + get + { + return _refKind; + } + } + + public override TypeWithAnnotations TypeWithAnnotations + { + get + { + if (_lazyType == null) + { + var diagnostics = DiagnosticBag.GetInstance(); + var result = this.ComputeType(binder: null, CSharpSyntaxNode, diagnostics); + if (Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(result), null) == null) + { + this.AddDeclarationDiagnostics(diagnostics); + } + diagnostics.Free(); + } + + return _lazyType.Value; + } + } + + internal bool HasPointerType + { + get + { + if (_lazyType != null) + { + + var hasPointerType = _lazyType.Value.DefaultType.IsPointerOrFunctionPointer(); + Debug.Assert(hasPointerType == HasPointerTypeSyntactically); + return hasPointerType; + } + + return HasPointerTypeSyntactically; + } + } + + protected abstract bool HasPointerTypeSyntactically { get; } + + /// + /// To facilitate lookup, all indexer symbols have the same name. + /// Check the MetadataName property to find the name that will be + /// emitted (based on IndexerNameAttribute, or the default "Item"). + /// + public override string Name + { + get + { + return _name; + } + } + + public override string MetadataName + { + get + { + // Explicit implementation names may have spaces if the interface + // is generic (between the type arguments). + return _sourceName.Replace(" ", ""); + } + } + + public override Symbol ContainingSymbol + { + get + { + return _containingType; + } + } + + public override NamedTypeSymbol ContainingType + { + get + { + return _containingType; + } + } + + internal override LexicalSortKey GetLexicalSortKey() + { + return new LexicalSortKey(Location, this.DeclaringCompilation); + } + + public override ImmutableArray Locations + { + get + { + return ImmutableArray.Create(Location); + } + } + + public override ImmutableArray DeclaringSyntaxReferences + { + get + { + return ImmutableArray.Create(_syntaxRef); + } + } + + public override bool IsAbstract + { + get { return (_modifiers & DeclarationModifiers.Abstract) != 0; } + } + + public override bool IsExtern + { + get { return (_modifiers & DeclarationModifiers.Extern) != 0; } + } + + public override bool IsStatic + { + get { return (_modifiers & DeclarationModifiers.Static) != 0; } + } + + internal bool IsFixed + { + get { return false; } + } + + /// + /// Even though it is declared with an IndexerDeclarationSyntax, an explicit + /// interface implementation is not an indexer because it will not cause the + /// containing type to be emitted with a DefaultMemberAttribute (and even if + /// there is another indexer, the name of the explicit implementation won't + /// match). This is important for round-tripping. + /// + public override bool IsIndexer + { + get { return (_modifiers & DeclarationModifiers.Indexer) != 0; } + } + + public override bool IsOverride + { + get { return (_modifiers & DeclarationModifiers.Override) != 0; } + } + + public override bool IsSealed + { + get { return (_modifiers & DeclarationModifiers.Sealed) != 0; } + } + + public override bool IsVirtual + { + get { return (_modifiers & DeclarationModifiers.Virtual) != 0; } + } + + internal bool IsNew + { + get { return (_modifiers & DeclarationModifiers.New) != 0; } + } + + internal bool HasReadOnlyModifier => (_modifiers & DeclarationModifiers.ReadOnly) != 0; + + public override MethodSymbol GetMethod + { + get { return _getMethod; } + } + + public override MethodSymbol SetMethod + { + get { return _setMethod; } + } + + internal override Microsoft.Cci.CallingConvention CallingConvention + { + get { return (IsStatic ? 0 : Microsoft.Cci.CallingConvention.HasThis); } + } + + public override ImmutableArray Parameters + { + get + { + if (_lazyParameters.IsDefault) + { + var diagnostics = DiagnosticBag.GetInstance(); + var result = this.ComputeParameters(binder: null, CSharpSyntaxNode, diagnostics); + if (ImmutableInterlocked.InterlockedInitialize(ref _lazyParameters, result)) + { + this.AddDeclarationDiagnostics(diagnostics); + } + diagnostics.Free(); + } + + return _lazyParameters; + } + } + + internal override bool IsExplicitInterfaceImplementation + => (_propertyFlags & Flags.IsExplicitInterfaceImplementation) != 0; + + public override ImmutableArray ExplicitInterfaceImplementations + { + get { return _explicitInterfaceImplementations; } + } + + public override ImmutableArray RefCustomModifiers + { + get { return _refCustomModifiers; } + } + + public override Accessibility DeclaredAccessibility + { + get + { + return ModifierUtils.EffectiveAccessibility(_modifiers); + } + } + + public bool HasSkipLocalsInitAttribute + { + get + { + var data = this.GetDecodedWellKnownAttributeData(); + return data?.HasSkipLocalsInitAttribute == true; + } + } + + internal bool IsAutoProperty + => (_propertyFlags & Flags.IsAutoProperty) != 0; + + /// + /// Backing field for automatically implemented property, or + /// for a property with an initializer. + /// + internal SynthesizedBackingFieldSymbol BackingField { get; } + + internal override bool MustCallMethodsDirectly + { + get { return false; } + } + + internal SyntaxReference SyntaxReference + { + get + { + return _syntaxRef; + } + } + + internal CSharpSyntaxNode CSharpSyntaxNode + { + get + { + return (CSharpSyntaxNode)_syntaxRef.GetSyntax(); + } + } + + public abstract SyntaxList AttributeDeclarationSyntaxList { get; } + + internal SyntaxTree SyntaxTree + { + get + { + return _syntaxRef.SyntaxTree; + } + } + + internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) + { + Location location = TypeLocation; + var compilation = DeclaringCompilation; + + Debug.Assert(location != null); + + // Check constraints on return type and parameters. Note: Dev10 uses the + // property name location for any such errors. We'll do the same for return + // type errors but for parameter errors, we'll use the parameter location. + + if ((object)_explicitInterfaceType != null) + { + var explicitInterfaceSpecifier = GetExplicitInterfaceSpecifier(this.CSharpSyntaxNode); + Debug.Assert(explicitInterfaceSpecifier != null); + _explicitInterfaceType.CheckAllConstraints(compilation, conversions, new SourceLocation(explicitInterfaceSpecifier.Name), diagnostics); + + // Note: we delayed nullable-related checks that could pull on NonNullTypes + if (!_explicitInterfaceImplementations.IsEmpty) + { + TypeSymbol.CheckNullableReferenceTypeMismatchOnImplementingMember(this.ContainingType, this, _explicitInterfaceImplementations[0], isExplicit: true, diagnostics); + } + } + + if (_refKind == RefKind.RefReadOnly) + { + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + + if (Type.ContainsNativeInteger()) + { + compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + + if (compilation.ShouldEmitNullableAttributes(this) && + this.TypeWithAnnotations.NeedsNullableAttribute()) + { + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + } + + private void CheckAccessibility(Location location, DiagnosticBag diagnostics, bool isExplicitInterfaceImplementation) + { + var info = ModifierUtils.CheckAccessibility(_modifiers, this, isExplicitInterfaceImplementation); + if (info != null) + { + diagnostics.Add(new CSDiagnostic(info, location)); + } + } + +#nullable enable + protected abstract DeclarationModifiers MakeModifiers( + SyntaxTokenList modifiers, bool isExplicitInterfaceImplementation, + bool isIndexer, bool accessorsHaveImplementation, + Location location, DiagnosticBag diagnostics, out bool modifierErrors); +#nullable restore + + + + private void CheckModifiers(bool isExplicitInterfaceImplementation, Location location, bool isIndexer, DiagnosticBag diagnostics) + { + bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface; + + if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride)) + { + diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); + } + else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) + { + // A static member '{0}' cannot be marked as override, virtual, or abstract + diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); + } + else if (IsStatic && HasReadOnlyModifier) + { + // Static member '{0}' cannot be marked 'readonly'. + diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); + } + else if (IsOverride && (IsNew || IsVirtual)) + { + // A member '{0}' marked as override cannot be marked as new or virtual + diagnostics.Add(ErrorCode.ERR_OverrideNotNew, location, this); + } + else if (IsSealed && !IsOverride && !(IsAbstract && isExplicitInterfaceImplementationInInterface)) + { + // '{0}' cannot be sealed because it is not an override + diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this); + } + else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct) + { + // The modifier '{0}' is not valid for this item + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.AbstractKeyword)); + } + else if (IsVirtual && ContainingType.TypeKind == TypeKind.Struct) + { + // The modifier '{0}' is not valid for this item + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.VirtualKeyword)); + } + else if (IsAbstract && IsExtern) + { + diagnostics.Add(ErrorCode.ERR_AbstractAndExtern, location, this); + } + else if (IsAbstract && IsSealed && !isExplicitInterfaceImplementationInInterface) + { + diagnostics.Add(ErrorCode.ERR_AbstractAndSealed, location, this); + } + else if (IsAbstract && IsVirtual) + { + diagnostics.Add(ErrorCode.ERR_AbstractNotVirtual, location, this.Kind.Localize(), this); + } + else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride) + { + diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this); + } + else if (ContainingType.IsStatic && !IsStatic) + { + ErrorCode errorCode = isIndexer ? ErrorCode.ERR_IndexerInStaticClass : ErrorCode.ERR_InstanceMemberInStaticClass; + diagnostics.Add(errorCode, location, this); + } + } + +#nullable enable + protected abstract SourcePropertyAccessorSymbol? CreateAccessorSymbol( + bool isGet, + CSharpSyntaxNode? syntaxOpt, + PropertySymbol? explicitlyImplementedPropertyOpt, + string aliasQualifierOpt, + bool isAutoPropertyAccessor, + bool isExplicitInterfaceImplementation, + DiagnosticBag diagnostics); + + protected abstract SourcePropertyAccessorSymbol CreateExpressionBodiedAccessor( + ArrowExpressionClauseSyntax syntax, + PropertySymbol? explicitlyImplementedPropertyOpt, + string aliasQualifierOpt, + bool isExplicitInterfaceImplementation, + DiagnosticBag diagnostics); +#nullable restore + + private void CheckAccessibilityMoreRestrictive(SourcePropertyAccessorSymbol accessor, DiagnosticBag diagnostics) + { + if (((object)accessor != null) && + !IsAccessibilityMoreRestrictive(this.DeclaredAccessibility, accessor.LocalAccessibility)) + { + diagnostics.Add(ErrorCode.ERR_InvalidPropertyAccessMod, accessor.Locations[0], accessor, this); + } + } + + /// + /// Return true if the accessor accessibility is more restrictive + /// than the property accessibility, otherwise false. + /// + private static bool IsAccessibilityMoreRestrictive(Accessibility property, Accessibility accessor) + { + if (accessor == Accessibility.NotApplicable) + { + return true; + } + return (accessor < property) && + ((accessor != Accessibility.Protected) || (property != Accessibility.Internal)); + } + + private static void CheckAbstractPropertyAccessorNotPrivate(SourcePropertyAccessorSymbol accessor, DiagnosticBag diagnostics) + { + if (accessor.LocalAccessibility == Accessibility.Private) + { + diagnostics.Add(ErrorCode.ERR_PrivateAbstractAccessor, accessor.Locations[0], accessor); + } + } + + public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) + { + ref var lazyDocComment = ref expandIncludes ? ref _lazyExpandedDocComment : ref _lazyDocComment; + return SourceDocumentationCommentUtils.GetAndCacheDocumentationComment(this, expandIncludes, ref lazyDocComment); + } + + // Separate these checks out of FindExplicitlyImplementedProperty because they depend on the accessor symbols, + // which depend on the explicitly implemented property + private void CheckExplicitImplementationAccessor(MethodSymbol thisAccessor, MethodSymbol otherAccessor, PropertySymbol explicitlyImplementedProperty, DiagnosticBag diagnostics) + { + var thisHasAccessor = (object)thisAccessor != null; + var otherHasAccessor = otherAccessor.IsImplementable(); + + if (otherHasAccessor && !thisHasAccessor) + { + diagnostics.Add(ErrorCode.ERR_ExplicitPropertyMissingAccessor, this.Location, this, otherAccessor); + } + else if (!otherHasAccessor && thisHasAccessor) + { + diagnostics.Add(ErrorCode.ERR_ExplicitPropertyAddingAccessor, thisAccessor.Locations[0], thisAccessor, explicitlyImplementedProperty); + } + else if (TypeSymbol.HaveInitOnlyMismatch(thisAccessor, otherAccessor)) + { + Debug.Assert(thisAccessor.MethodKind == MethodKind.PropertySet); + diagnostics.Add(ErrorCode.ERR_ExplicitPropertyMismatchInitOnly, thisAccessor.Locations[0], thisAccessor, otherAccessor); + } + } + + internal override OverriddenOrHiddenMembersResult OverriddenOrHiddenMembers + { + get + { + if (_lazyOverriddenOrHiddenMembers == null) + { + Interlocked.CompareExchange(ref _lazyOverriddenOrHiddenMembers, this.MakeOverriddenOrHiddenMembers(), null); + } + return _lazyOverriddenOrHiddenMembers; + } + } + + /// + /// If this property is sealed, then we have to emit both accessors - regardless of whether + /// they are present in the source - so that they can be marked final. (i.e. sealed). + /// + internal SynthesizedSealedPropertyAccessor SynthesizedSealedAccessorOpt + { + get + { + bool hasGetter = (object)_getMethod != null; + bool hasSetter = (object)_setMethod != null; + if (!this.IsSealed || (hasGetter && hasSetter)) + { + return null; + } + + // This has to be cached because the CCI layer depends on reference equality. + // However, there's no point in having more than one field, since we don't + // expect to have to synthesize more than one accessor. + if ((object)_lazySynthesizedSealedAccessor == null) + { + Interlocked.CompareExchange(ref _lazySynthesizedSealedAccessor, MakeSynthesizedSealedAccessor(), null); + } + return _lazySynthesizedSealedAccessor; + } + } + + /// + /// Only non-null for sealed properties without both accessors. + /// + private SynthesizedSealedPropertyAccessor MakeSynthesizedSealedAccessor() + { + Debug.Assert(this.IsSealed && ((object)_getMethod == null || (object)_setMethod == null)); + + if ((object)_getMethod != null) + { + // need to synthesize setter + MethodSymbol overriddenAccessor = this.GetOwnOrInheritedSetMethod(); + return (object)overriddenAccessor == null ? null : new SynthesizedSealedPropertyAccessor(this, overriddenAccessor); + } + else if ((object)_setMethod != null) + { + // need to synthesize getter + MethodSymbol overriddenAccessor = this.GetOwnOrInheritedGetMethod(); + return (object)overriddenAccessor == null ? null : new SynthesizedSealedPropertyAccessor(this, overriddenAccessor); + } + else + { + // Arguably, it would be more correct to return an array containing two + // synthesized accessors, but we're already in an error case, so we'll + // minimize the cascading error behavior by suppressing synthesis. + return null; + } + } + + #region Attributes + + IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => this; + + AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.Property; + + AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations + => (_propertyFlags & Flags.IsAutoProperty) != 0 + ? AttributeLocation.Property | AttributeLocation.Field + : AttributeLocation.Property; + + /// + /// Returns a bag of custom attributes applied on the property and data decoded from well-known attributes. Returns null if there are no attributes. + /// + /// + /// Forces binding and decoding of attributes. + /// + private CustomAttributesBag GetAttributesBag() + { + var bag = _lazyCustomAttributesBag; + if (bag != null && bag.IsSealed) + { + return bag; + } + + // The property is responsible for completion of the backing field + _ = BackingField?.GetAttributes(); + + if (LoadAndValidateAttributes(OneOrMany.Create(AttributeDeclarationSyntaxList), ref _lazyCustomAttributesBag)) + { + var completed = _state.NotePartComplete(CompletionPart.Attributes); + Debug.Assert(completed); + } + + Debug.Assert(_lazyCustomAttributesBag.IsSealed); + return _lazyCustomAttributesBag; + } + + /// + /// Gets the attributes applied on this symbol. + /// Returns an empty array if there are no attributes. + /// + /// + /// NOTE: This method should always be kept as a sealed override. + /// If you want to override attribute binding logic for a sub-class, then override method. + /// + public sealed override ImmutableArray GetAttributes() + { + return this.GetAttributesBag().Attributes; + } + + /// + /// Returns data decoded from well-known attributes applied to the symbol or null if there are no applied attributes. + /// + /// + /// Forces binding and decoding of attributes. + /// + private PropertyWellKnownAttributeData GetDecodedWellKnownAttributeData() + { + var attributesBag = _lazyCustomAttributesBag; + if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed) + { + attributesBag = this.GetAttributesBag(); + } + + return (PropertyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData; + } + + /// + /// Returns data decoded from special early bound well-known attributes applied to the symbol or null if there are no applied attributes. + /// + /// + /// Forces binding and decoding of attributes. + /// + internal PropertyEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData() + { + var attributesBag = _lazyCustomAttributesBag; + if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) + { + attributesBag = this.GetAttributesBag(); + } + + return (PropertyEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData; + } + + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + + var compilation = this.DeclaringCompilation; + var type = this.TypeWithAnnotations; + + if (type.Type.ContainsDynamic()) + { + AddSynthesizedAttribute(ref attributes, + compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length + RefCustomModifiers.Length, _refKind)); + } + + if (type.Type.ContainsNativeInteger()) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNativeIntegerAttribute(this, type.Type)); + } + + if (type.Type.ContainsTupleNames()) + { + AddSynthesizedAttribute(ref attributes, + compilation.SynthesizeTupleNamesAttribute(type.Type)); + } + + if (compilation.ShouldEmitNullableAttributes(this)) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, ContainingType.GetNullableContextValue(), type)); + } + + if (this.ReturnsByRefReadonly) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); + } + } + + internal sealed override bool IsDirectlyExcludedFromCodeCoverage => + GetDecodedWellKnownAttributeData()?.HasExcludeFromCodeCoverageAttribute == true; + + internal override bool HasSpecialName + { + get + { + var data = GetDecodedWellKnownAttributeData(); + return data != null && data.HasSpecialNameAttribute; + } + } + + internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) + { + CSharpAttributeData boundAttribute; + ObsoleteAttributeData obsoleteData; + + if (EarlyDecodeDeprecatedOrExperimentalOrObsoleteAttribute(ref arguments, out boundAttribute, out obsoleteData)) + { + if (obsoleteData != null) + { + arguments.GetOrCreateData().ObsoleteAttributeData = obsoleteData; + } + + return boundAttribute; + } + + if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.IndexerNameAttribute)) + { + bool hasAnyDiagnostics; + boundAttribute = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); + if (!boundAttribute.HasErrors) + { + string indexerName = boundAttribute.CommonConstructorArguments[0].DecodeValue(SpecialType.System_String); + if (indexerName != null) + { + arguments.GetOrCreateData().IndexerName = indexerName; + } + + if (!hasAnyDiagnostics) + { + return boundAttribute; + } + } + + return null; + } + + return base.EarlyDecodeWellKnownAttribute(ref arguments); + } + + /// + /// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute. + /// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet. + /// + internal override ObsoleteAttributeData ObsoleteAttributeData + { + get + { + if (!_containingType.AnyMemberHasAttributes) + { + return null; + } + + var lazyCustomAttributesBag = _lazyCustomAttributesBag; + if (lazyCustomAttributesBag != null && lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) + { + return ((PropertyEarlyWellKnownAttributeData)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData)?.ObsoleteAttributeData; + } + + return ObsoleteAttributeData.Uninitialized; + } + } + + internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments arguments) + { + Debug.Assert(arguments.AttributeSyntaxOpt != null); + + var attribute = arguments.Attribute; + Debug.Assert(!attribute.HasErrors); + Debug.Assert(arguments.SymbolPart == AttributeLocation.None); + + if (attribute.IsTargetAttribute(this, AttributeDescription.IndexerNameAttribute)) + { + //NOTE: decoding was done by EarlyDecodeWellKnownAttribute. + ValidateIndexerNameAttribute(attribute, arguments.AttributeSyntaxOpt, arguments.Diagnostics); + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.SpecialNameAttribute)) + { + arguments.GetOrCreateData().HasSpecialNameAttribute = true; + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.ExcludeFromCodeCoverageAttribute)) + { + arguments.GetOrCreateData().HasExcludeFromCodeCoverageAttribute = true; + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.SkipLocalsInitAttribute)) + { + CSharpAttributeData.DecodeSkipLocalsInitAttribute(DeclaringCompilation, ref arguments); + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.DynamicAttribute)) + { + // DynamicAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitDynamicAttr, arguments.AttributeSyntaxOpt.Location); + } + else if (ReportExplicitUseOfReservedAttributes(in arguments, + ReservedAttributes.DynamicAttribute | ReservedAttributes.IsReadOnlyAttribute | ReservedAttributes.IsUnmanagedAttribute | ReservedAttributes.IsByRefLikeAttribute | ReservedAttributes.TupleElementNamesAttribute | ReservedAttributes.NullableAttribute | ReservedAttributes.NativeIntegerAttribute)) + { + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.DisallowNullAttribute)) + { + arguments.GetOrCreateData().HasDisallowNullAttribute = true; + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.AllowNullAttribute)) + { + arguments.GetOrCreateData().HasAllowNullAttribute = true; + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.MaybeNullAttribute)) + { + arguments.GetOrCreateData().HasMaybeNullAttribute = true; + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.NotNullAttribute)) + { + arguments.GetOrCreateData().HasNotNullAttribute = true; + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.MemberNotNullAttribute)) + { + MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); + CSharpAttributeData.DecodeMemberNotNullAttribute(ContainingType, ref arguments); + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.MemberNotNullWhenAttribute)) + { + MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); + CSharpAttributeData.DecodeMemberNotNullWhenAttribute(ContainingType, ref arguments); + } + } + + internal bool HasDisallowNull + { + get + { + var data = GetDecodedWellKnownAttributeData(); + return data != null && data.HasDisallowNullAttribute; + } + } + + internal bool HasAllowNull + { + get + { + var data = GetDecodedWellKnownAttributeData(); + return data != null && data.HasAllowNullAttribute; + } + } + + internal bool HasMaybeNull + { + get + { + var data = GetDecodedWellKnownAttributeData(); + return data != null && data.HasMaybeNullAttribute; + } + } + + internal bool HasNotNull + { + get + { + var data = GetDecodedWellKnownAttributeData(); + return data != null && data.HasNotNullAttribute; + } + } + + internal SourceAttributeData DisallowNullAttributeIfExists + => FindAttribute(AttributeDescription.DisallowNullAttribute); + + internal SourceAttributeData AllowNullAttributeIfExists + => FindAttribute(AttributeDescription.AllowNullAttribute); + + internal SourceAttributeData MaybeNullAttributeIfExists + => FindAttribute(AttributeDescription.MaybeNullAttribute); + + internal SourceAttributeData NotNullAttributeIfExists + => FindAttribute(AttributeDescription.NotNullAttribute); + + internal ImmutableArray MemberNotNullAttributeIfExists + => FindAttributes(AttributeDescription.MemberNotNullAttribute); + + internal ImmutableArray MemberNotNullWhenAttributeIfExists + => FindAttributes(AttributeDescription.MemberNotNullWhenAttribute); + + private SourceAttributeData FindAttribute(AttributeDescription attributeDescription) + => (SourceAttributeData)GetAttributes().First(a => a.IsTargetAttribute(this, attributeDescription)); + + private ImmutableArray FindAttributes(AttributeDescription attributeDescription) + => GetAttributes().Where(a => a.IsTargetAttribute(this, attributeDescription)).Cast().ToImmutableArray(); + + internal override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) + { + Debug.Assert(!boundAttributes.IsDefault); + Debug.Assert(!allAttributeSyntaxNodes.IsDefault); + Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length); + Debug.Assert(_lazyCustomAttributesBag != null); + Debug.Assert(_lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed); + Debug.Assert(symbolPart == AttributeLocation.None); + + base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData); + } + + private void ValidateIndexerNameAttribute(CSharpAttributeData attribute, AttributeSyntax node, DiagnosticBag diagnostics) + { + if (!this.IsIndexer || this.IsExplicitInterfaceImplementation) + { + diagnostics.Add(ErrorCode.ERR_BadIndexerNameAttr, node.Name.Location, node.GetErrorDisplayName()); + } + else + { + string indexerName = attribute.CommonConstructorArguments[0].DecodeValue(SpecialType.System_String); + if (indexerName == null || !SyntaxFacts.IsValidIdentifier(indexerName)) + { + diagnostics.Add(ErrorCode.ERR_BadArgumentToAttribute, node.ArgumentList.Arguments[0].Location, node.GetErrorDisplayName()); + } + } + } + + #endregion + + #region Completion + + internal sealed override bool RequiresCompletion + { + get { return true; } + } + + internal sealed override bool HasComplete(CompletionPart part) + { + return _state.HasComplete(part); + } + + internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken) + { + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + var incompletePart = _state.NextIncompletePart; + switch (incompletePart) + { + case CompletionPart.Attributes: + GetAttributes(); + break; + + case CompletionPart.StartPropertyParameters: + case CompletionPart.FinishPropertyParameters: + { + if (_state.NotePartComplete(CompletionPart.StartPropertyParameters)) + { + var parameters = this.Parameters; + if (parameters.Length > 0) + { + var diagnostics = DiagnosticBag.GetInstance(); + var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary); + foreach (var parameter in this.Parameters) + { + parameter.ForceComplete(locationOpt, cancellationToken); + parameter.Type.CheckAllConstraints(DeclaringCompilation, conversions, parameter.Locations[0], diagnostics); + } + + this.AddDeclarationDiagnostics(diagnostics); + diagnostics.Free(); + } + + DeclaringCompilation.SymbolDeclaredEvent(this); + var completedOnThisThread = _state.NotePartComplete(CompletionPart.FinishPropertyParameters); + Debug.Assert(completedOnThisThread); + } + else + { + // StartPropertyParameters was completed by another thread. Wait for it to finish the parameters. + _state.SpinWaitComplete(CompletionPart.FinishPropertyParameters, cancellationToken); + } + } + break; + + case CompletionPart.StartPropertyType: + case CompletionPart.FinishPropertyType: + { + if (_state.NotePartComplete(CompletionPart.StartPropertyType)) + { + var diagnostics = DiagnosticBag.GetInstance(); + var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary); + this.Type.CheckAllConstraints(DeclaringCompilation, conversions, Location, diagnostics); + + var type = this.Type; + if (type.IsRestrictedType(ignoreSpanLikeTypes: true)) + { + diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeLocation, type); + } + else if (this.IsAutoProperty && type.IsRefLikeType && (this.IsStatic || !this.ContainingType.IsRefLikeType)) + { + diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeLocation, type); + } + + this.AddDeclarationDiagnostics(diagnostics); + var completedOnThisThread = _state.NotePartComplete(CompletionPart.FinishPropertyType); + Debug.Assert(completedOnThisThread); + diagnostics.Free(); + } + else + { + // StartPropertyType was completed by another thread. Wait for it to finish the type. + _state.SpinWaitComplete(CompletionPart.FinishPropertyType, cancellationToken); + } + } + break; + + case CompletionPart.None: + return; + + default: + // any other values are completion parts intended for other kinds of symbols + _state.NotePartComplete(CompletionPart.All & ~CompletionPart.PropertySymbolAll); + break; + } + + _state.SpinWaitComplete(incompletePart, cancellationToken); + } + } + + #endregion + +#nullable enable + protected abstract ImmutableArray ComputeParameters(Binder? binder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics); + + protected abstract TypeWithAnnotations ComputeType(Binder? binder, SyntaxNode syntax, DiagnosticBag diagnostics); + + protected abstract ExplicitInterfaceSpecifierSyntax? GetExplicitInterfaceSpecifier(SyntaxNode syntax); + + protected abstract BaseParameterListSyntax? GetParameterListSyntax(CSharpSyntaxNode syntax); +#nullable restore + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs index db9ea5a9197..db971383e14 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs @@ -7,338 +7,144 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Reflection; using Microsoft.Cci; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal sealed class SynthesizedRecordPropertySymbol : SourceOrRecordPropertySymbol + internal sealed class SynthesizedRecordPropertySymbol : SourcePropertySymbolBase, IAttributeTargetSymbol { - private readonly ParameterSymbol _backingParameter; - internal override SynthesizedBackingFieldSymbol BackingField { get; } - public override MethodSymbol GetMethod { get; } - public override MethodSymbol SetMethod { get; } - public override NamedTypeSymbol ContainingType { get; } + public ParameterSymbol BackingParameter { get; } public SynthesizedRecordPropertySymbol( - NamedTypeSymbol containingType, + SourceMemberContainerTypeSymbol containingType, ParameterSymbol backingParameter, DiagnosticBag diagnostics) - : base(backingParameter.Locations[0]) + : base(containingType, + binder: null, + backingParameter.GetNonNullSyntaxNode(), + RefKind.None, + backingParameter.Name, + backingParameter.Locations[0], + diagnostics) { - ContainingType = containingType; - _backingParameter = backingParameter; - string name = backingParameter.Name; - BackingField = new SynthesizedBackingFieldSymbol( - this, - GeneratedNames.MakeBackingFieldName(name), - isReadOnly: true, - isStatic: false, - hasInitializer: true); - GetMethod = new GetAccessorSymbol(this, name); - SetMethod = new InitAccessorSymbol(this, name, diagnostics); + BackingParameter = backingParameter; } - public ParameterSymbol BackingParameter => _backingParameter; - - internal override bool IsAutoProperty => true; - - public override RefKind RefKind => RefKind.None; - - public override TypeWithAnnotations TypeWithAnnotations => _backingParameter.TypeWithAnnotations; - - public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; - - public override ImmutableArray Parameters => ImmutableArray.Empty; - - public override bool IsIndexer => false; - - public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; - - public override Symbol ContainingSymbol => ContainingType; - - public override ImmutableArray Locations => _backingParameter.Locations; - - public override ImmutableArray DeclaringSyntaxReferences => _backingParameter.DeclaringSyntaxReferences; - - public override Accessibility DeclaredAccessibility => Accessibility.Public; - - public override bool IsStatic => false; - - public override bool IsVirtual => false; + IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => this; - public override bool IsOverride => false; + AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AttributeLocation.None; - public override bool IsAbstract => false; + AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.None; - public override bool IsSealed => false; + protected override Location TypeLocation + => ((ParameterSyntax)CSharpSyntaxNode).Type!.Location; - public override bool IsExtern => false; + protected override SyntaxTokenList GetModifierTokens(SyntaxNode syntax) + => new SyntaxTokenList(); - internal override bool HasSpecialName => false; + protected override ArrowExpressionClauseSyntax? GetArrowExpression(SyntaxNode syntax) + => null; - internal override CallingConvention CallingConvention => CallingConvention.HasThis; + protected override bool HasInitializer(SyntaxNode syntax) + => true; // Synthesized record properties always have a synthesized initializer - internal override bool MustCallMethodsDirectly => false; + public override SyntaxList AttributeDeclarationSyntaxList + => new SyntaxList(); - internal override ObsoleteAttributeData? ObsoleteAttributeData => null; - - public override string Name => _backingParameter.Name; - - protected override IAttributeTargetSymbol AttributesOwner => this; - - protected override AttributeLocation AllowedAttributeLocations => AttributeLocation.None; - - protected override AttributeLocation DefaultAttributeLocation => AttributeLocation.None; - - public override ImmutableArray GetAttributes() => ImmutableArray.Empty; - - internal override bool HasPointerType => Type.IsPointerType(); - - public override SyntaxList AttributeDeclarationSyntaxList => new SyntaxList(); - - private sealed class GetAccessorSymbol : SynthesizedInstanceMethodSymbol + protected override void GetAccessorDeclarations( + CSharpSyntaxNode syntax, + DiagnosticBag diagnostics, + out bool isAutoProperty, + out bool hasAccessorList, + out bool accessorsHaveImplementation, + out bool isInitOnly, + out CSharpSyntaxNode? getSyntax, + out CSharpSyntaxNode? setSyntax) { - private readonly SynthesizedRecordPropertySymbol _property; - - public override string Name { get; } - - public GetAccessorSymbol(SynthesizedRecordPropertySymbol property, string paramName) - { - _property = property; - Name = SourcePropertyAccessorSymbol.GetAccessorName( - paramName, - getNotSet: true, - isWinMdOutput: false /* unused for getters */); - } - - public override MethodKind MethodKind => MethodKind.PropertyGet; - - public override int Arity => 0; - - public override bool IsExtensionMethod => false; - - public override bool HidesBaseMethodsByName => false; - - public override bool IsVararg => false; - - public override bool ReturnsVoid => false; - - public override bool IsAsync => false; - - public override RefKind RefKind => RefKind.None; - - public override TypeWithAnnotations ReturnTypeWithAnnotations => _property.TypeWithAnnotations; - - public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - - public override ImmutableArray TypeArgumentsWithAnnotations => ImmutableArray.Empty; - - public override ImmutableArray TypeParameters => ImmutableArray.Empty; - - public override ImmutableArray Parameters => _property.Parameters; - - public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; - - public override ImmutableArray RefCustomModifiers => _property.RefCustomModifiers; - - public override Symbol AssociatedSymbol => _property; - - public override Symbol ContainingSymbol => _property.ContainingSymbol; - - public override ImmutableArray Locations => _property.Locations; - - public override Accessibility DeclaredAccessibility => _property.DeclaredAccessibility; - - public override bool IsStatic => _property.IsStatic; - - public override bool IsVirtual => _property.IsVirtual; - - public override bool IsOverride => _property.IsOverride; - - public override bool IsAbstract => _property.IsAbstract; - - public override bool IsSealed => _property.IsSealed; - - public override bool IsExtern => _property.IsExtern; - - public override ImmutableHashSet ReturnNotNullIfParameterNotNull => ImmutableHashSet.Empty; - - internal override bool HasSpecialName => _property.HasSpecialName; - - internal override MethodImplAttributes ImplementationAttributes => MethodImplAttributes.Managed; - - internal override bool HasDeclarativeSecurity => false; - - internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null; - - internal override bool RequiresSecurityObject => false; - - internal override CallingConvention CallingConvention => CallingConvention.HasThis; - - internal override bool GenerateDebugInfo => false; - - public override DllImportData? GetDllImportData() => null; - - internal override ImmutableArray GetAppliedConditionalSymbols() - => ImmutableArray.Empty; - - internal override IEnumerable GetSecurityInformation() - => Array.Empty(); - - internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; - - internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => false; - - internal override bool SynthesizesLoweredBoundBody => true; - - internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) - { - // Method body: - // - // { - // return this.<>backingField; - // } - - var F = new SyntheticBoundNodeFactory(this, this.GetNonNullSyntaxNode(), compilationState, diagnostics); - - F.CurrentFunction = this; - F.CloseMethod(F.Block(F.Return(F.Field(F.This(), _property.BackingField)))); - } + isAutoProperty = true; + hasAccessorList = false; + getSyntax = setSyntax = syntax; + isInitOnly = true; + accessorsHaveImplementation = false; } - private sealed class InitAccessorSymbol : SynthesizedInstanceMethodSymbol + protected override void CheckForBlockAndExpressionBody(CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { - private readonly SynthesizedRecordPropertySymbol _property; - - public override TypeWithAnnotations ReturnTypeWithAnnotations { get; } - public override string Name { get; } - - public InitAccessorSymbol( - SynthesizedRecordPropertySymbol property, - string paramName, - DiagnosticBag diagnostics) - { - _property = property; - Name = SourcePropertyAccessorSymbol.GetAccessorName( - paramName, - getNotSet: false, - // https://github.com/dotnet/roslyn/issues/44684 - isWinMdOutput: false); - - var comp = property.DeclaringCompilation; - var type = TypeWithAnnotations.Create(comp.GetSpecialType(SpecialType.System_Void)); - var initOnlyType = Binder.GetWellKnownType( - comp, - WellKnownType.System_Runtime_CompilerServices_IsExternalInit, - diagnostics, - property.Location); - var modifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(initOnlyType)); - - ReturnTypeWithAnnotations = type.WithModifiers(modifiers); - } - - internal override bool IsInitOnly => true; - - public override MethodKind MethodKind => MethodKind.PropertySet; - - public override int Arity => 0; - - public override bool IsExtensionMethod => false; - - public override bool HidesBaseMethodsByName => false; - - public override bool IsVararg => false; - - public override bool ReturnsVoid => true; - - public override bool IsAsync => false; - - public override RefKind RefKind => RefKind.None; - - public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + // Nothing to do here + } - public override ImmutableArray TypeArgumentsWithAnnotations => ImmutableArray.Empty; + protected override DeclarationModifiers MakeModifiers( + SyntaxTokenList modifiers, + bool isExplicitInterfaceImplementation, + bool isIndexer, + bool accessorsHaveImplementation, + Location location, + DiagnosticBag diagnostics, + out bool modifierErrors) + { + Debug.Assert(!isExplicitInterfaceImplementation); + Debug.Assert(!isIndexer); + modifierErrors = false; - public override ImmutableArray TypeParameters => ImmutableArray.Empty; + return DeclarationModifiers.Public; + } - public override ImmutableArray Parameters => ImmutableArray.Create(SynthesizedParameterSymbol.Create( + protected override SourcePropertyAccessorSymbol CreateAccessorSymbol( + bool isGet, + CSharpSyntaxNode? syntax, + PropertySymbol? explicitlyImplementedPropertyOpt, + string aliasQualifierOpt, + bool isAutoPropertyAccessor, + bool isExplicitInterfaceImplementation, + DiagnosticBag diagnostics) + { + Debug.Assert(syntax is object); + Debug.Assert(isAutoPropertyAccessor); + return SourcePropertyAccessorSymbol.CreateAccessorSymbol( + isGet, + usesInit: !isGet, // the setter is always init-only + ContainingType, this, - _property.TypeWithAnnotations, - ordinal: 0, - RefKind.None, - name: ParameterSymbol.ValueParameterName)); - - public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; - - public override ImmutableArray RefCustomModifiers => _property.RefCustomModifiers; - - public override Symbol AssociatedSymbol => _property; - - public override Symbol ContainingSymbol => _property.ContainingSymbol; - - public override ImmutableArray Locations => _property.Locations; - - public override Accessibility DeclaredAccessibility => _property.DeclaredAccessibility; - - public override bool IsStatic => _property.IsStatic; - - public override bool IsVirtual => _property.IsVirtual; - - public override bool IsOverride => _property.IsOverride; - - public override bool IsAbstract => _property.IsAbstract; - - public override bool IsSealed => _property.IsSealed; - - public override bool IsExtern => _property.IsExtern; - - public override ImmutableHashSet ReturnNotNullIfParameterNotNull => ImmutableHashSet.Empty; - - internal override bool HasSpecialName => _property.HasSpecialName; - - internal override MethodImplAttributes ImplementationAttributes => MethodImplAttributes.Managed; - - internal override bool HasDeclarativeSecurity => false; - - internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null; - - internal override bool RequiresSecurityObject => false; - - internal override CallingConvention CallingConvention => CallingConvention.HasThis; - - internal override bool GenerateDebugInfo => false; - - public override DllImportData? GetDllImportData() => null; - - internal override ImmutableArray GetAppliedConditionalSymbols() - => ImmutableArray.Empty; - - internal override IEnumerable GetSecurityInformation() - => Array.Empty(); + _modifiers, + _sourceName, + ((ParameterSyntax)syntax).Identifier.GetLocation(), + syntax, + diagnostics); + } - internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; + protected override SourcePropertyAccessorSymbol CreateExpressionBodiedAccessor( + ArrowExpressionClauseSyntax syntax, + PropertySymbol? explicitlyImplementedPropertyOpt, + string aliasQualifierOpt, + bool isExplicitInterfaceImplementation, + DiagnosticBag diagnostics) + { + // There should be no expression-bodied synthesized record properties + throw ExceptionUtilities.Unreachable; + } - internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => false; + protected override ImmutableArray ComputeParameters(Binder? binder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics) + { + return ImmutableArray.Empty; + } - internal override bool SynthesizesLoweredBoundBody => true; + protected override TypeWithAnnotations ComputeType(Binder? binder, SyntaxNode syntax, DiagnosticBag diagnostics) + { + return BackingParameter.TypeWithAnnotations; + } - internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) - { - // Method body: - // - // { - // this.<>backingField = value; - // } + protected override bool HasPointerTypeSyntactically + // Since we already bound the type, don't bother looking at syntax + => TypeWithAnnotations.DefaultType.IsPointerOrFunctionPointer(); - var F = new SyntheticBoundNodeFactory(this, this.GetNonNullSyntaxNode(), compilationState, diagnostics); + protected override ExplicitInterfaceSpecifierSyntax? GetExplicitInterfaceSpecifier(SyntaxNode syntax) + => null; - F.CurrentFunction = this; - F.CloseMethod(F.Block( - F.Assignment(F.Field(F.This(), _property.BackingField), F.Parameter(Parameters[0])), - F.Return())); - } - } + protected override BaseParameterListSyntax? GetParameterListSyntax(CSharpSyntaxNode syntax) + => null; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs index ad685f2096d..e410ef762d6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs @@ -32,7 +32,7 @@ internal override FlowAnalysisAnnotations FlowAnalysisAnnotations get { var result = FlowAnalysisAnnotations.None; - if (ContainingSymbol is SourcePropertyAccessorSymbol propertyAccessor && propertyAccessor.AssociatedSymbol is SourcePropertySymbol property) + if (ContainingSymbol is SourcePropertyAccessorSymbol propertyAccessor && propertyAccessor.AssociatedSymbol is SourcePropertySymbolBase property) { if (property.HasDisallowNull) { @@ -78,7 +78,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - if (ContainingSymbol is SourcePropertyAccessorSymbol propertyAccessor && propertyAccessor.AssociatedSymbol is SourcePropertySymbol property) + if (ContainingSymbol is SourcePropertyAccessorSymbol propertyAccessor && propertyAccessor.AssociatedSymbol is SourcePropertySymbolBase property) { var annotations = FlowAnalysisAnnotations; if ((annotations & FlowAnalysisAnnotations.DisallowNull) != 0) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs index 9fb78b198a4..40e379ec1dd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs @@ -16,13 +16,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal sealed class SynthesizedBackingFieldSymbol : FieldSymbolWithAttributesAndModifiers { - private readonly SourceOrRecordPropertySymbol _property; + private readonly SourcePropertySymbolBase _property; private readonly string _name; internal bool HasInitializer { get; } protected override DeclarationModifiers Modifiers { get; } public SynthesizedBackingFieldSymbol( - SourceOrRecordPropertySymbol property, + SourcePropertySymbolBase property, string name, bool isReadOnly, bool isStatic, diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 2627940b0f6..beb318ec8f8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -10232,5 +10232,22 @@ public static void Main() 303 "); } + + [Fact] + public void SynthesizedRecordPointerProperty() + { + var src = @" +record R(int P1, int* P2, delegate* P3);"; + + var comp = CreateCompilation(src); + var p = comp.GlobalNamespace.GetTypeMember("R").GetMember("P1"); + Assert.False(p.HasPointerType); + + p = comp.GlobalNamespace.GetTypeMember("R").GetMember("P2"); + Assert.True(p.HasPointerType); + + p = comp.GlobalNamespace.GetTypeMember("R").GetMember("P3"); + Assert.True(p.HasPointerType); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index d0a4724aab1..ee06c00cb4c 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -146,12 +146,13 @@ public void GeneratedProperties() comp.VerifyDiagnostics(); var c = comp.GlobalNamespace.GetTypeMember("C"); - var x = (SourceOrRecordPropertySymbol)c.GetProperty("x"); + var x = (SourcePropertySymbolBase)c.GetProperty("x"); Assert.NotNull(x.GetMethod); Assert.Equal(MethodKind.PropertyGet, x.GetMethod.MethodKind); Assert.Equal(SpecialType.System_Int32, x.Type.SpecialType); Assert.False(x.IsReadOnly); Assert.False(x.IsWriteOnly); + Assert.False(x.IsImplicitlyDeclared); Assert.Equal(Accessibility.Public, x.DeclaredAccessibility); Assert.False(x.IsVirtual); Assert.False(x.IsStatic); @@ -162,26 +163,30 @@ public void GeneratedProperties() Assert.Equal(x, backing.AssociatedSymbol); Assert.Equal(c, backing.ContainingSymbol); Assert.Equal(c, backing.ContainingType); + Assert.True(backing.IsImplicitlyDeclared); var getAccessor = x.GetMethod; Assert.Equal(x, getAccessor.AssociatedSymbol); + Assert.True(getAccessor.IsImplicitlyDeclared); Assert.Equal(c, getAccessor.ContainingSymbol); Assert.Equal(c, getAccessor.ContainingType); Assert.Equal(Accessibility.Public, getAccessor.DeclaredAccessibility); var setAccessor = x.SetMethod; Assert.Equal(x, setAccessor.AssociatedSymbol); + Assert.True(setAccessor.IsImplicitlyDeclared); Assert.Equal(c, setAccessor.ContainingSymbol); Assert.Equal(c, setAccessor.ContainingType); Assert.Equal(Accessibility.Public, setAccessor.DeclaredAccessibility); Assert.True(setAccessor.IsInitOnly); - var y = (SourceOrRecordPropertySymbol)c.GetProperty("y"); + var y = (SourcePropertySymbolBase)c.GetProperty("y"); Assert.NotNull(y.GetMethod); Assert.Equal(MethodKind.PropertyGet, y.GetMethod.MethodKind); Assert.Equal(SpecialType.System_Int32, y.Type.SpecialType); Assert.False(y.IsReadOnly); Assert.False(y.IsWriteOnly); + Assert.False(y.IsImplicitlyDeclared); Assert.Equal(Accessibility.Public, y.DeclaredAccessibility); Assert.False(x.IsVirtual); Assert.False(x.IsStatic); @@ -192,14 +197,17 @@ public void GeneratedProperties() Assert.Equal(y, backing.AssociatedSymbol); Assert.Equal(c, backing.ContainingSymbol); Assert.Equal(c, backing.ContainingType); + Assert.True(backing.IsImplicitlyDeclared); getAccessor = y.GetMethod; Assert.Equal(y, getAccessor.AssociatedSymbol); + Assert.True(getAccessor.IsImplicitlyDeclared); Assert.Equal(c, getAccessor.ContainingSymbol); Assert.Equal(c, getAccessor.ContainingType); setAccessor = y.SetMethod; Assert.Equal(y, setAccessor.AssociatedSymbol); + Assert.True(setAccessor.IsImplicitlyDeclared); Assert.Equal(c, setAccessor.ContainingSymbol); Assert.Equal(c, setAccessor.ContainingType); Assert.Equal(Accessibility.Public, setAccessor.DeclaredAccessibility); -- GitLab