Skip to content

Commit 560bfe3

Browse files
Clarify how to get CompilationEnd rule violations to surface in IDE (#21)
* switch from warnings to errors for rule violations * IncludeAssemblyInVSIXContainer * add to README * bump version
1 parent 4f8ca99 commit 560bfe3

File tree

5 files changed

+52
-30
lines changed

5 files changed

+52
-30
lines changed

README.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,48 @@ Although IntelliSense in your IDE guides the available SDK decorators and their
1818

1919
### Technical Primer
2020

21-
When you upload your project's built assembly to the ODC Portal, it does not have access to the underlying code—the ODC Portal checks compliance with the rules by reflecting on assembly's metadata.
21+
When you upload your project's built assembly to the ODC Portal, it does not have access to the underlying code—the ODC Portal checks compliance with the rules by reflecting on the assembly's metadata.
2222

2323
This component, built from scratch, implements the rules using the rich code analysis APIs of [Roslyn](https://github.com/dotnet/roslyn), the .NET compiler.
2424

25+
#### Analyzer phases
26+
27+
The analyzer operates in two distinct phases, registered through the Roslyn `AnalysisContext`:
28+
29+
1. **Symbol analysis phase** – Triggered by `RegisterSymbolAction`, this phase performs immediate syntax and semantic analysis on individual declarations as you type. For example, when you declare a method, the analyzer instantly checks if its name starts with an underscore and reports a violation if it does (`NameBeginsWithUnderscoresRule`). These diagnostics appear immediately in your IDE's Problems window.
30+
31+
2. **Compilation end phase** – Registered via `RegisterCompilationEndAction`, this phase runs after all symbols have been processed and the semantic model is complete. For example, it ensures exactly one `[OSInterface]` exists across your entire codebase by maintaining a `ConcurrentDictionary` of interface declarations and validating their uniqueness (`NoSingleInterfaceRule` or `ManyInterfacesRule`). These diagnostics may appear with a slight delay as they require complete semantic analysis.
32+
2533
## How to use
2634

2735
### Visual Studio 2022 (Enterprise, Pro and Community editions)
2836

2937
You can use the auto-updating extension from the Visual Studio Marketplace. Simply [install the extension from the Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=JonathanAlgar.CustomCodeAnalyzer).
3038

31-
If your project references the External Libraries SDK (`OutSystems.ExternalLibraries.SDK`), the extension should automatically start providing feedback on your code.
39+
If your project references the External Libraries SDK (`OutSystems.ExternalLibraries.SDK`), the extension should automatically start providing feedback on your code.
40+
41+
To ensure real-time feedback for [compilation end phase](#analyzer-phases) rules (not just during builds), you need to configure your Visual Studio's background analysis:
42+
43+
1. Select **Tools** > **Options**.
44+
1. From the left menu select **C#** > **Advanced**.
45+
1. Set both **Run background code analysis** for and **Show compiler errors and warnings** to **Entire solution**.
46+
1. Make sure the **Run code analysis in separate process box** is unchecked.
3247

3348
### Others
3449

3550
Add the [NuGet package](https://www.nuget.org/packages/CustomCode.Analyzer/) as a dev dependency to your ODC external libraries project:
3651

37-
```dotnet add package CustomCode.Analyzer```
52+
dotnet add package CustomCode.Analyzer
3853

3954
If your project references the External Libraries SDK (`OutSystems.ExternalLibraries.SDK`), the package should automatically start providing feedback on your code.
4055

56+
#### Visual Studio Code
57+
58+
To ensure real-time feedback for [compilation end phase](#analyzer-phases) rules (not just during builds), you need to configure your Visual Studio Code's background analysis:
59+
60+
1. Open the command palette (_Ctrl+Shift+P_).
61+
1. Search for "roslyn". Set the **Dotnet › Background Analysis: Analyzer Diagnostics Scope** to **fullSolution**.
62+
4163
> :bulb: Auto-updating extensions for Visual Studio Code and Rider are in the works.
4264
4365
## TODO

src/CustomCode-Analyzer.Vsix/CustomCode-Analyzer.Vsix.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<AssemblyName>CustomCode_Analyzer</AssemblyName>
2121
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
2222
<GeneratePkgDefFile>false</GeneratePkgDefFile>
23-
<IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
23+
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
2424
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
2525
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
2626
<CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory>

src/CustomCode-Analyzer.Vsix/source.extension.vsixmanifest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
33
<Metadata>
4-
<Identity Id="CustomCode_Analyzer.Vsix.e1c79fe3-d19f-4bf9-9c0c-57ba1f95b494" Version="0.1.3" Language="en-US" Publisher="Jonathan Algar"/>
4+
<Identity Id="CustomCode_Analyzer.Vsix.e1c79fe3-d19f-4bf9-9c0c-57ba1f95b494" Version="0.1.4" Language="en-US" Publisher="Jonathan Algar"/>
55
<DisplayName>ODC Custom Code Analyzer</DisplayName>
66
<Description xml:space="preserve">Get feedback on your OutSytems Developer Cloud (ODC) custom C# code as you code.</Description>
77
<MoreInfo>https://github.com/jonathanalgar/CustomCode-Analyzer</MoreInfo>

src/CustomCode-Analyzer/Analyzer.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static class Categories
6868
title: "Missing OSInterface declaration",
6969
messageFormat: "No OSInterface found - exactly one interface must be decorated with [OSInterface]",
7070
category: Categories.Design,
71-
defaultSeverity: DiagnosticSeverity.Warning,
71+
defaultSeverity: DiagnosticSeverity.Error,
7272
isEnabledByDefault: true,
7373
customTags: WellKnownDiagnosticTags.CompilationEnd,
7474
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05002");
@@ -78,7 +78,7 @@ public static class Categories
7878
title: "Multiple OSInterface declarations",
7979
messageFormat: "Multiple OSInterface attributes found: {0} - only one interface should have this attribute",
8080
category: Categories.Design,
81-
defaultSeverity: DiagnosticSeverity.Warning,
81+
defaultSeverity: DiagnosticSeverity.Error,
8282
isEnabledByDefault: true,
8383
customTags: WellKnownDiagnosticTags.CompilationEnd,
8484
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05003");
@@ -88,7 +88,7 @@ public static class Categories
8888
title: "Non-public OSInterface",
8989
messageFormat: "The OSInterface '{0}' must be public",
9090
category: Categories.Design,
91-
defaultSeverity: DiagnosticSeverity.Warning,
91+
defaultSeverity: DiagnosticSeverity.Error,
9292
isEnabledByDefault: true,
9393
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05004");
9494

@@ -97,7 +97,7 @@ public static class Categories
9797
title: "Non-instantiable interface",
9898
messageFormat: "The interface decorated with OSInterface is implemented by class '{0}' which doesn't have a public parameterless constructor",
9999
category: Categories.Design,
100-
defaultSeverity: DiagnosticSeverity.Warning,
100+
defaultSeverity: DiagnosticSeverity.Error,
101101
isEnabledByDefault: true,
102102
description: "Each class implementing an OSInterface-decorated interface must have a public parameterless constructor.",
103103
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05005");
@@ -107,7 +107,7 @@ public static class Categories
107107
title: "Missing implementation",
108108
messageFormat: "No class implementing the interface decorated with OSInterface '{0}' found in your file",
109109
category: Categories.Design,
110-
defaultSeverity: DiagnosticSeverity.Warning,
110+
defaultSeverity: DiagnosticSeverity.Error,
111111
isEnabledByDefault: true,
112112
description: "Each interface decorated with OSInterface must have an implementing class.",
113113
customTags: WellKnownDiagnosticTags.CompilationEnd,
@@ -118,7 +118,7 @@ public static class Categories
118118
title: "Empty interface",
119119
messageFormat: "No methods found in the interface decorated with OSInterface '{0}'",
120120
category: Categories.Design,
121-
defaultSeverity: DiagnosticSeverity.Warning,
121+
defaultSeverity: DiagnosticSeverity.Error,
122122
isEnabledByDefault: true,
123123
description: "The interface decorated with OSInterface must define at least one method.",
124124
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05007");
@@ -128,7 +128,7 @@ public static class Categories
128128
title: "Many implementation",
129129
messageFormat: "The interface decorated with OSInterface '{0}' is implemented by multiple classes: {1}",
130130
category: Categories.Design,
131-
defaultSeverity: DiagnosticSeverity.Warning,
131+
defaultSeverity: DiagnosticSeverity.Error,
132132
isEnabledByDefault: true,
133133
description: "Only one class should implement an interface decorated with OSInterface.",
134134
customTags: WellKnownDiagnosticTags.CompilationEnd,
@@ -141,7 +141,7 @@ public static class Categories
141141
title: "Non-public OSStructure",
142142
messageFormat: "The struct decorated with OSStructure '{0}' is not public",
143143
category: Categories.Design,
144-
defaultSeverity: DiagnosticSeverity.Warning,
144+
defaultSeverity: DiagnosticSeverity.Error,
145145
isEnabledByDefault: true,
146146
description: "Structs decorated with OSStructure must be public.",
147147
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05010");
@@ -151,7 +151,7 @@ public static class Categories
151151
title: "Non-public OSStructureField",
152152
messageFormat: "The property/field decorated by OSStructureField '{0}' in struct {1} is not public",
153153
category: Categories.Design,
154-
defaultSeverity: DiagnosticSeverity.Warning,
154+
defaultSeverity: DiagnosticSeverity.Error,
155155
isEnabledByDefault: true,
156156
description: "Properties and fields decorated with OSStructureField must be public.",
157157
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05011");
@@ -161,7 +161,7 @@ public static class Categories
161161
title: "Non-public field ignored",
162162
messageFormat: "The property/field decorated by OSIgnore '{0}' in struct '{1}' is not public",
163163
category: Categories.Design,
164-
defaultSeverity: DiagnosticSeverity.Warning,
164+
defaultSeverity: DiagnosticSeverity.Error,
165165
isEnabledByDefault: true,
166166
description: "Properties and fields decorated with OSIgnore must be public.",
167167
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05012");
@@ -171,7 +171,7 @@ public static class Categories
171171
title: "Empty structure",
172172
messageFormat: "No public properties/fields found in the struct decorated with OSStructure '{0}'",
173173
category: Categories.Design,
174-
defaultSeverity: DiagnosticSeverity.Warning,
174+
defaultSeverity: DiagnosticSeverity.Error,
175175
isEnabledByDefault: true,
176176
description: "Structs decorated with OSStructure must have at least one public property or field.",
177177
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05013");
@@ -183,7 +183,7 @@ public static class Categories
183183
title: "Unsupported parameter type in OSStructure",
184184
messageFormat: "The struct decorated with OSStructure '{0}' contains a public property/field that uses an unsupported parameter type '{1}'",
185185
category: Categories.Design,
186-
defaultSeverity: DiagnosticSeverity.Warning,
186+
defaultSeverity: DiagnosticSeverity.Error,
187187
isEnabledByDefault: true,
188188
description: "Public properties or fields in structs decorated with OSStructure must use supported types.",
189189
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05015");
@@ -193,7 +193,7 @@ public static class Categories
193193
title: "Unsupported ref parameter",
194194
messageFormat: "The parameter '{0}' in action '{1}' is passed by reference. Passing parameters by reference is not supported.",
195195
category: Categories.Design,
196-
defaultSeverity: DiagnosticSeverity.Warning,
196+
defaultSeverity: DiagnosticSeverity.Error,
197197
isEnabledByDefault: true,
198198
description: "Parameters in actions must be passed by value. Return modified values instead of using reference parameters.",
199199
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05016");
@@ -203,7 +203,7 @@ public static class Categories
203203
title: "Unsupported type mapping",
204204
messageFormat: "{0} has an incompatible DataType assigned and cannot be converted",
205205
category: Categories.Design,
206-
defaultSeverity: DiagnosticSeverity.Warning,
206+
defaultSeverity: DiagnosticSeverity.Error,
207207
isEnabledByDefault: true,
208208
description: "The DataType assigned to a property or field is incompatible with its corresponding .NET type. It can't be automatically converted to the specified OutSystems type..",
209209
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05017");
@@ -213,7 +213,7 @@ public static class Categories
213213
title: "Missing public implementation",
214214
messageFormat: "The class that implements the interface decorated with OSInterface '{0}' must be public",
215215
category: Categories.Design,
216-
defaultSeverity: DiagnosticSeverity.Warning,
216+
defaultSeverity: DiagnosticSeverity.Error,
217217
isEnabledByDefault: true,
218218
description: "Classes implementing interfaces decorated with OSInterface must be public.",
219219
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05018");
@@ -223,7 +223,7 @@ public static class Categories
223223
title: "Name exceeds maximum length",
224224
messageFormat: "The name '{0}' is not supported as it has more than 50 characters",
225225
category: Categories.Naming,
226-
defaultSeverity: DiagnosticSeverity.Warning,
226+
defaultSeverity: DiagnosticSeverity.Error,
227227
isEnabledByDefault: true,
228228
description: "Names must not exceed 50 characters in length.",
229229
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05019");
@@ -233,7 +233,7 @@ public static class Categories
233233
title: "Name begins with numbers",
234234
messageFormat: "The name '{0}' is not supported as it begins with a number",
235235
category: Categories.Naming,
236-
defaultSeverity: DiagnosticSeverity.Warning,
236+
defaultSeverity: DiagnosticSeverity.Error,
237237
isEnabledByDefault: true,
238238
description: "Names must not begin with a number. Use a letter as the first character.",
239239
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05020");
@@ -243,7 +243,7 @@ public static class Categories
243243
title: "Unsupported characters in a name",
244244
messageFormat: "The name '{0}' is not supported as it has the following invalid characters '{1}'",
245245
category: Categories.Naming,
246-
defaultSeverity: DiagnosticSeverity.Warning,
246+
defaultSeverity: DiagnosticSeverity.Error,
247247
isEnabledByDefault: true,
248248
description: "Names must only contain letters, numbers, and underscores.",
249249
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05021");
@@ -253,7 +253,7 @@ public static class Categories
253253
title: "Name begins with underscores",
254254
messageFormat: "The {0} name '{1}' should not begin with underscores",
255255
category: Categories.Naming,
256-
defaultSeverity: DiagnosticSeverity.Warning,
256+
defaultSeverity: DiagnosticSeverity.Error,
257257
isEnabledByDefault: true,
258258
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05022");
259259

@@ -264,7 +264,7 @@ public static class Categories
264264
title: "Missing structure decoration",
265265
messageFormat: "The struct '{0}' used as '{1}' is missing OSStructure decoration",
266266
category: Categories.Design,
267-
defaultSeverity: DiagnosticSeverity.Warning,
267+
defaultSeverity: DiagnosticSeverity.Error,
268268
isEnabledByDefault: true,
269269
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05024");
270270

@@ -273,7 +273,7 @@ public static class Categories
273273
title: "Duplicated name",
274274
messageFormat: "More than one object with name '{0}' was found",
275275
category: Categories.Naming,
276-
defaultSeverity: DiagnosticSeverity.Warning,
276+
defaultSeverity: DiagnosticSeverity.Error,
277277
isEnabledByDefault: true,
278278
customTags: WellKnownDiagnosticTags.CompilationEnd,
279279
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05025");
@@ -283,7 +283,7 @@ public static class Categories
283283
title: "Unsupported default value",
284284
messageFormat: "The default value specified for {0} is unsupported",
285285
category: Categories.Design,
286-
defaultSeverity: DiagnosticSeverity.Warning,
286+
defaultSeverity: DiagnosticSeverity.Error,
287287
isEnabledByDefault: true,
288288
description: "Default values for parameters must be compile-time constants of supported types.",
289289
helpLinkUri: "https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05026");

src/CustomCode-Analyzer/CustomCode-Analyzer.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<LangVersion>latest</LangVersion>
99
<RootNamespace>CustomCode_Analyzer</RootNamespace>
1010
<PackageId>CustomCode.Analyzer</PackageId>
11-
<Version>0.1.3</Version>
12-
<AssemblyVersion>0.1.3</AssemblyVersion>
13-
<FileVersion>0.1.3</FileVersion>
11+
<Version>0.1.4</Version>
12+
<AssemblyVersion>0.1.4</AssemblyVersion>
13+
<FileVersion>0.1.4</FileVersion>
1414
<Authors>Jonathan Algar</Authors>
1515
<Product>OutSystems Developer Cloud (ODC) Custom Code Analyzer</Product>
1616
<Description>Get feedback on your OutSytems Developer Cloud (ODC) custom C# code as you code.</Description>

0 commit comments

Comments
 (0)