|
11 | 11 | using System.Threading.Tasks; |
12 | 12 | using System.IO; |
13 | 13 | using Pchp.CodeAnalysis.Utilities; |
| 14 | +using Devsense.PHP.Syntax.Ast; |
| 15 | +using Devsense.PHP.Syntax; |
14 | 16 |
|
15 | 17 | namespace Pchp.CodeAnalysis.CommandLine |
16 | 18 | { |
@@ -165,6 +167,7 @@ internal override CommandLineArguments CommonParse(IEnumerable<string> args, str |
165 | 167 | var additionalFiles = new List<CommandLineSourceFile>(); |
166 | 168 | var embeddedFiles = new List<CommandLineSourceFile>(); |
167 | 169 | var managedResources = new List<ResourceDescription>(); |
| 170 | + var globalAttributes = new List<AttributeElement>(); |
168 | 171 | var defines = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); |
169 | 172 | string outputDirectory = baseDirectory; |
170 | 173 | string subDirectory = null; |
@@ -707,8 +710,24 @@ internal override CommandLineArguments CommonParse(IEnumerable<string> args, str |
707 | 710 | break; |
708 | 711 |
|
709 | 712 | case "attr": |
| 713 | + |
710 | 714 | // FQN("value1","value2") |
711 | 715 |
|
| 716 | + if (string.IsNullOrWhiteSpace(value)) |
| 717 | + { |
| 718 | + diagnostics.Add(Errors.MessageProvider.Instance.CreateDiagnostic(Errors.ErrorCode.ERR_SwitchNeedsValue, Location.None, name)); |
| 719 | + break; |
| 720 | + } |
| 721 | + |
| 722 | + if (TryParseAttributeSpec(value.Trim(), out var attr)) |
| 723 | + { |
| 724 | + globalAttributes.Add(attr); |
| 725 | + } |
| 726 | + else |
| 727 | + { |
| 728 | + diagnostics.Add(Errors.MessageProvider.Instance.CreateDiagnostic(Errors.ErrorCode.ERR_BadCompilationOptionValue, Location.None, name, value)); |
| 729 | + } |
| 730 | + |
712 | 731 | break; |
713 | 732 |
|
714 | 733 | default: |
@@ -997,6 +1016,102 @@ private static void ParseDefine(string value, Dictionary<string, string> defines |
997 | 1016 | } |
998 | 1017 | } |
999 | 1018 |
|
| 1019 | + private static bool TryParseAttributeSpec(string value, out AttributeElement attr) |
| 1020 | + { |
| 1021 | + attr = null; |
| 1022 | + |
| 1023 | + ReadOnlySpan<char> fqn; |
| 1024 | + var signature = new List<ActualParam>(); |
| 1025 | + var span = Devsense.PHP.Text.Span.Invalid; |
| 1026 | + |
| 1027 | + // FQN("value1","value2") |
| 1028 | + var parenIdx = value.IndexOf('('); |
| 1029 | + if (parenIdx >= 0) |
| 1030 | + { |
| 1031 | + fqn = value.AsSpan(0, parenIdx); |
| 1032 | + |
| 1033 | + var args = value.AsSpan(parenIdx).Trim(); |
| 1034 | + |
| 1035 | + bool ConsumeChar(ref ReadOnlySpan<char> text, char ch) |
| 1036 | + { |
| 1037 | + if (text.Length != 0 && text[0] == ch) |
| 1038 | + { |
| 1039 | + text = text.Slice(1); |
| 1040 | + return true; |
| 1041 | + } |
| 1042 | + return false; |
| 1043 | + } |
| 1044 | + |
| 1045 | + bool ConsumeArg(ref ReadOnlySpan<char> text, out ActualParam p) |
| 1046 | + { |
| 1047 | + p = default(ActualParam); |
| 1048 | + |
| 1049 | + if (!ConsumeChar(ref text, '"')) return false; |
| 1050 | + |
| 1051 | + var str = new StringBuilder(); |
| 1052 | + var escaped = false; |
| 1053 | + var closed = false; |
| 1054 | + while (text.Length != 0) |
| 1055 | + { |
| 1056 | + var ch = text[0]; |
| 1057 | + text = text.Slice(1); |
| 1058 | + |
| 1059 | + if (escaped) { } |
| 1060 | + else if (ch == '\\') { escaped = true; continue; } |
| 1061 | + else if (ch == '"') { closed = true; break; } |
| 1062 | + |
| 1063 | + str.Append(ch); |
| 1064 | + } |
| 1065 | + |
| 1066 | + if (!closed) |
| 1067 | + { |
| 1068 | + return false; |
| 1069 | + } |
| 1070 | + |
| 1071 | + p = new ActualParam( |
| 1072 | + Devsense.PHP.Text.Span.Invalid, |
| 1073 | + new StringLiteral(Devsense.PHP.Text.Span.Invalid, str.ToString()) |
| 1074 | + ); |
| 1075 | + return true; |
| 1076 | + } |
| 1077 | + |
| 1078 | + if (ConsumeChar(ref args, '(')) |
| 1079 | + { |
| 1080 | + do |
| 1081 | + { |
| 1082 | + if (ConsumeChar(ref args, ')')) break; |
| 1083 | + if (ConsumeChar(ref args, ',')) continue; |
| 1084 | + if (ConsumeArg(ref args, out var p)) |
| 1085 | + { |
| 1086 | + signature.Add(p); |
| 1087 | + continue; |
| 1088 | + } |
| 1089 | + break; |
| 1090 | + } while (args.Length != 0); |
| 1091 | + } |
| 1092 | + |
| 1093 | + if (args.IsWhiteSpace() == false) |
| 1094 | + return false; // unexpected |
| 1095 | + } |
| 1096 | + else |
| 1097 | + { |
| 1098 | + fqn = value.AsSpan(); |
| 1099 | + } |
| 1100 | + |
| 1101 | + if (fqn.IsWhiteSpace()) |
| 1102 | + { |
| 1103 | + return false; |
| 1104 | + } |
| 1105 | + |
| 1106 | + // |
| 1107 | + attr = new AttributeElement( |
| 1108 | + span, |
| 1109 | + new ClassTypeRef(span, QualifiedName.Parse(fqn.Trim().ToString().Replace('.', QualifiedName.Separator), true)), |
| 1110 | + new CallSignature(signature, span) |
| 1111 | + ); |
| 1112 | + return true; |
| 1113 | + } |
| 1114 | + |
1000 | 1115 | internal override void GenerateErrorForNoFilesFoundInRecurse(string path, IList<Diagnostic> errors) |
1001 | 1116 | { |
1002 | 1117 | // nothing |
|
0 commit comments