public ImmutableArray <Diagnostic> Diagnostics => _diagnostics != null?_diagnostics.ToImmutableArray() : ImmutableArray <Diagnostic> .Empty; // Save an allocation if no errors were found public void Error(Span span, ErrorInfo info, params string[] argsOpt) { if (_diagnostics == null) { _diagnostics = new List <Diagnostic>(); } _diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(_syntaxTree, span, info, argsOpt)); }
/// <summary> /// Collects declaration diagnostics. /// </summary> internal void GetDiagnostics(DiagnosticBag diagnostic) { // check functions duplicity var funcs = this.Functions; if (funcs.Length > 1) { var set = new HashSet <QualifiedName>(); // handle unconditionally declared functions: foreach (var f in funcs) { if (f.IsConditional) { continue; } if (!set.Add(f.QualifiedName)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(_syntaxTree, ((FunctionDecl)f.Syntax).HeadingSpan, Devsense.PHP.Errors.FatalErrors.FunctionRedeclared, f.QualifiedName.ToString())); } } //// handle conditionally declared functions: // NOTE: commented since we should allow to compile it //foreach (var f in funcs.Where(f => f.IsConditional)) //{ // if (set.Contains(f.QualifiedName)) // does not make sense to declare function if it is declared already unconditionally // { // diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(_syntaxTree, ((FunctionDecl)f.Syntax).HeadingSpan, Devsense.PHP.Errors.FatalErrors.FunctionRedeclared, f.PhpName)); // } //} } // check class/interface duplicity: var types = this.ContainedTypes; if (types.Count > 1) { var set = new HashSet <QualifiedName>(); foreach (var t in types) { if (t.Syntax.IsConditional) { continue; } if (!set.Add(t.FullName)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(_syntaxTree, t.Syntax.HeadingSpan, Devsense.PHP.Errors.FatalErrors.TypeRedeclared, t.FullName.ToString())); } } } }
internal void ValidateReferences(CommonCompilation compilation, DiagnosticBag diagnostics) { foreach (AssemblyIdentity identity in compilation.ReferencedAssemblyNames) { if (CommonScriptEngine.IsReservedAssemblyName(identity)) { DiagnosticBagExtensions.Add(diagnostics, ErrorCode.ERR_ReservedAssemblyName, (Location)null, new object[1] { (object)identity.GetDisplayName(false) }); } } }
public ImmutableArray <Diagnostic> Diagnostics => _diagnostics != null?_diagnostics.ToImmutableArray() : ImmutableArray <Diagnostic> .Empty; // Save an allocation if no errors were found public void Error(Span span, ErrorInfo info, params string[] argsOpt) { if (info == FatalErrors.ParentAccessedInParentlessClass) { // ignore PHP2070: we'll handle it more precisely, also we might recover from it // otherwise we'll report it again in our diagnostics return; } if (_diagnostics == null) { _diagnostics = new List <Diagnostic>(); } _diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(_syntaxTree, span, info, argsOpt)); }
/// <summary> /// Collects declaration diagnostics. /// </summary> public virtual void GetDiagnostics(DiagnosticBag diagnostic) { // check mandatory behind and optional parameter bool foundopt = false; foreach (var p in SyntaxSignature.FormalParams) { if (p.InitValue == null) { if (foundopt && !p.IsVariadic) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, p.Span, Devsense.PHP.Errors.Warnings.MandatoryBehindOptionalParam, "$" + p.Name.Name.Value)); } } else { foundopt = true; } } }
void Add(Devsense.PHP.Text.Span span, Devsense.PHP.Errors.ErrorInfo err, params string[] args) { _diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(_routine, span, err, args)); }
public override void GetDiagnostics(DiagnosticBag diagnostic) { var name = _syntax.Name.Name; // diagnostics: if (name.Value.StartsWith("__", StringComparison.Ordinal)) { // magic methods: if (name.IsConstructName) // __construct() { if (IsStatic) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Errors.ConstructCannotBeStatic, _type.FullName.ToString())); } if (_syntax.ReturnType != null) { // {0} cannot declare a return type diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_CannotDeclareReturnType, "Constructor " + _type.FullName.ToString(name, false)); } } else if (name.IsDestructName) // __destruct() { if (_syntax.Signature.FormalParams.Length != 0) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Errors.DestructCannotTakeArguments, _type.FullName.ToString())); } if (IsStatic) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Errors.DestructCannotBeStatic, _type.FullName.ToString())); } if (_syntax.ReturnType != null) { // {0} cannot declare a return type diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_CannotDeclareReturnType, "Destructor " + _type.FullName.ToString(name, false)); } } else if (name.IsToStringName) // __tostring() { if ((IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.MagicMethodMustBePublicNonStatic, name.Value)); } if (_syntax.Signature.FormalParams.Length != 0) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Errors.MethodCannotTakeArguments, _type.FullName.ToString(), name.Value)); } } else if (name.IsCloneName) // __clone() { if (IsStatic) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Errors.CloneCannotBeStatic, _type.FullName.ToString())); } if (_syntax.Signature.FormalParams.Length != 0) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Errors.CloneCannotTakeArguments, _type.FullName.ToString())); } if (_syntax.ReturnType != null) { // {0} cannot declare a return type diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_CannotDeclareReturnType, _type.FullName.ToString(name, false)); } } else if (name.IsCallName) // __call($name, $args) { if (IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.MagicMethodMustBePublicNonStatic, name.Value)); } if (_syntax.Signature.FormalParams.Length != 2) { diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_MustTakeArgs, "Method", _type.FullName.ToString(name, false), 2); } } else if (name.IsCallStaticName) // __callstatic($name, $args) { if (!IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.CallStatMustBePublicStatic)); } if (_syntax.Signature.FormalParams.Length != 2) { diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_MustTakeArgs, "Method", _type.FullName.ToString(name, false), 2); } } else if (name == Devsense.PHP.Syntax.Name.SpecialMethodNames.Set) // __set($name, $value) { if ((IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.MagicMethodMustBePublicNonStatic, name.Value)); } if (_syntax.Signature.FormalParams.Length != 2) { diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_MustTakeArgs, "Method", _type.FullName.ToString(name, false), 2); } } else if (name == Devsense.PHP.Syntax.Name.SpecialMethodNames.Get) // __get($name) { if ((IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.MagicMethodMustBePublicNonStatic, name.Value)); } if (_syntax.Signature.FormalParams.Length != 1) { diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_MustTakeArgs, "Method", _type.FullName.ToString(name, false), 1); } } else if (name == Devsense.PHP.Syntax.Name.SpecialMethodNames.Isset) // __isset($name) { if ((IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.MagicMethodMustBePublicNonStatic, name.Value)); } if (_syntax.Signature.FormalParams.Length != 1) { diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_MustTakeArgs, "Method", _type.FullName.ToString(name, false), 1); } } else if (name == Devsense.PHP.Syntax.Name.SpecialMethodNames.Unset) // __unset($name) { if ((IsStatic || (_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) != PhpMemberAttributes.Public)) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.ParametersSpan, Devsense.PHP.Errors.Warnings.MagicMethodMustBePublicNonStatic, name.Value)); } if (_syntax.Signature.FormalParams.Length != 1) { diagnostic.Add(this, _syntax.ReturnType.Span.ToTextSpan(), Errors.ErrorCode.ERR_MustTakeArgs, "Method", _type.FullName.ToString(name, false), 1); } } // ... } if (_syntax.Modifiers.IsAbstract()) { // abstract member in non-abstract class if ((_type.Syntax.MemberAttributes & (PhpMemberAttributes.Abstract | PhpMemberAttributes.Trait)) == 0) // not abstract nor trait { // TODO: ERR_AbstractMethodInNonAbstractClass } // abstract private if ((_syntax.Modifiers & PhpMemberAttributes.VisibilityMask) == PhpMemberAttributes.Private) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.HeadingSpan, Devsense.PHP.Errors.Errors.AbstractPrivateMethodDeclared)); } // abstract final if ((_syntax.Modifiers & PhpMemberAttributes.Final) != 0) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.HeadingSpan, Devsense.PHP.Errors.Errors.AbstractFinalMethodDeclared)); } // abstract method with body if (_syntax.Body != null) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.HeadingSpan, _type.IsInterface ? Devsense.PHP.Errors.Errors.InterfaceMethodWithBody : Devsense.PHP.Errors.Errors.AbstractMethodWithBody, _type.FullName.ToString(), name.Value)); } } else { if (_syntax.Body == null && !_type.IsInterface) { diagnostic.Add(DiagnosticBagExtensions.ParserDiagnostic(this, _syntax.HeadingSpan, Devsense.PHP.Errors.Errors.NonAbstractMethodWithoutBody, _type.FullName.ToString(), name.Value)); } } // base.GetDiagnostics(diagnostic); }
/// <summary> /// Matches all methods that can be overriden (non-static, public or protected, abstract or virtual) /// within this type sub-tree (this type, its base and interfaces) /// with its override. /// Methods without an override are either abstract or a ghost stup has to be synthesized. /// </summary> /// <param name="diagnostics"></param> internal OverrideInfo[] ResolveOverrides(DiagnosticBag diagnostics) { if (_lazyOverrides != null) { // already resolved return(_lazyOverrides); } // inherit abstracts from base type // ignoring System.Object (we don't override its methods from PHP) var overrides = new List <OverrideInfo>(); if (BaseType != null && BaseType.SpecialType != SpecialType.System_Object) { overrides.AddRange(BaseType.ResolveOverrides(diagnostics)); } // collect this type declared methods including synthesized methods var members = this.GetMembers(); // resolve overrides of inherited members for (int i = 0; i < overrides.Count; i++) { var m = overrides[i]; if (m.HasOverride == false) { // update override info of the inherited member overrides[i] = new OverrideInfo(m.Method, OverrideHelper.ResolveMethodImplementation(m.Method, members)); } else { // clear the interface flag of inherited override info m.ImplementsInterface = false; overrides[i] = m; } } // resolve overrides of interface methods foreach (var iface in Interfaces) { // skip interfaces implemented by base type or other interfaces, // we don't want to add redundant override entries: if ((BaseType != null && BaseType.ImplementsInterface(iface)) || Interfaces.Any(x => x != iface && x.ImplementsInterface(iface))) { // iface is already handled within overrides => skip // note: iface can be ignored in metadata at all actually continue; } var iface_abstracts = iface.ResolveOverrides(diagnostics); foreach (var m in iface_abstracts) { if (BaseType != null && m.Method.ContainingType != iface && BaseType.ImplementsInterface(m.Method.ContainingType)) { // iface {m.Method.ContainingType} already handled within overrides => skip continue; } // ignore interface method that is already implemented: if (overrides.Any(o => OverrideHelper.SignaturesMatch(o.Method, m.Method))) { continue; } // add interface member, // resolve its override overrides.Add(new OverrideInfo(m.Method, this.IsInterface ? null : OverrideHelper.ResolveMethodImplementation(m.Method, this)) { ImplementsInterface = true }); } } // add overrideable routines from this type foreach (var s in members) { if (s is MethodSymbol m && m.IsOverrideable()) { overrides.Add(new OverrideInfo(m)); } } // handle unresolved abstracts for (int i = 0; i < overrides.Count; i++) { var m = overrides[i]; if (m.IsUnresolvedAbstract && this is SourceTypeSymbol srct && !this.IsInterface) { if (!this.IsAbstract) { // Class '{0}' doesn't implement abstract method {1}::{2}() diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(srct.ContainingFile.SyntaxTree, srct.Syntax.HeadingSpan, Devsense.PHP.Errors.Errors.AbstractMethodNotImplemented, srct.FullName.ToString(), ((IPhpTypeSymbol)m.Method.ContainingType).FullName.ToString(), m.RoutineName)); } else if (m.ImplementsInterface /*&& this.IsAbstract*/) { m.ImplementsInterface = false; var method = m.Method; Debug.Assert(!method.IsStatic); Debug.Assert(method.DeclaredAccessibility != Accessibility.Private); Debug.Assert(method.ContainingType.IsInterface); // Template: abstract function {name}({parameters}) var ghost = new SynthesizedMethodSymbol(this, method.RoutineName, isstatic: false, isvirtual: true, isabstract: true, isfinal: false, returnType: method.ReturnType, accessibility: method.DeclaredAccessibility); ghost.SetParameters(SynthesizedParameterSymbol.Create(ghost, method.Parameters)); //module.SynthesizedManager.AddMethod(this, ghost); // will be added to synthesized manager by FinalizeMethodTable m.Method = ghost; // replace the interface method with synthesized abstract method // update overrides overrides[i] = m; } } } // cache & return return(_lazyOverrides = overrides.ToArray()); }
/// <summary> /// Matches all methods that can be overriden (non-static, public or protected, abstract or virtual) /// within this type sub-tree (this type, its base and interfaces) /// with its override. /// Methods without an override are either abstract or a ghost stup has to be synthesized. /// </summary> /// <param name="diagnostics"></param> internal OverrideInfo[] ResolveOverrides(DiagnosticBag diagnostics) { if (_lazyOverrides != null) { // already resolved return(_lazyOverrides); } // TODO: ignore System.Object ? // inherit abstracts from base type var overrides = new List <OverrideInfo>(); if (BaseType != null) { overrides.AddRange(BaseType.ResolveOverrides(diagnostics)); } // collect this type declared methods including synthesized methods var methods = this.GetMembers().OfType <MethodSymbol>(); var methodslookup = methods.Where(OverrideHelper.CanOverride).ToLookup(m => m.RoutineName, StringComparer.OrdinalIgnoreCase); // resolve overrides of inherited members for (int i = 0; i < overrides.Count; i++) { var m = overrides[i]; if (m.HasOverride == false) { // update override info of the inherited member overrides[i] = new OverrideInfo(m.Method, OverrideHelper.ResolveMethodImplementation(m.Method, methodslookup[m.RoutineName])); } else { // clear the interface flag of inherited override info m.ImplementsInterface = false; overrides[i] = m; } } // resolve overrides of interface methods foreach (var iface in Interfaces) { // skip interfaces implemented by base type or other interfaces, // we don't want to add redundant override entries: if ((BaseType != null && BaseType.ImplementsInterface(iface)) || Interfaces.Any(x => x != iface && x.ImplementsInterface(iface))) { // iface is already handled within overrides => skip // note: iface can be ignored in metadata at all actually continue; } var iface_abstracts = iface.ResolveOverrides(diagnostics); foreach (var m in iface_abstracts) { if (BaseType != null && m.Method.ContainingType != iface && BaseType.ImplementsInterface(m.Method.ContainingType)) { // iface {m.Method.ContainingType} already handled within overrides => skip continue; } // ignore interface method that is already implemented: if (overrides.Any(o => OverrideHelper.SignaturesMatch(o.Method, m.Method))) { continue; } // add interface member, // resolve its override overrides.Add(new OverrideInfo(m.Method, this.IsInterface ? null : OverrideHelper.ResolveMethodImplementation(m.Method, this)) { ImplementsInterface = true }); } } // add overrideable routines from this type foreach (var m in methods) { if (m.IsOverrideable()) { overrides.Add(new OverrideInfo(m)); } } // report unresolved abstracts if (!this.IsAbstract && !this.IsInterface && this is SourceTypeSymbol srct) { foreach (var m in overrides) { if (m.IsUnresolvedAbstract) { // Class '{0}' doesn't implement abstract method {1}::{2}() diagnostics.Add(DiagnosticBagExtensions.ParserDiagnostic(srct.ContainingFile.SyntaxTree, srct.Syntax.HeadingSpan, Devsense.PHP.Errors.Errors.AbstractMethodNotImplemented, srct.FullName.ToString(), ((IPhpTypeSymbol)m.Method.ContainingType).FullName.ToString(), m.RoutineName)); } } } // cache & return return(_lazyOverrides = overrides.ToArray()); }