Repository navigation
Description
What's wrong
SourceFileTemplate inherits Attributes from TemplateBase. AddSourceFile writes them first, through AddTemplate, and then writes the namespace and the usings:
CodeBlocker/CodeBlocker/Templates/SourceFileTemplate.cs
Lines 50 to 53 in f86da28
| codeBlocker.AddTemplate(template); | |
| codeBlocker.WriteFileScopedNamespace(template.Namespace); | |
| codeBlocker.WriteUsings(template.Usings); | |
| codeBlocker.AddClasses(template.Classes); |
codeBlocker.AddTemplate(template); // comments, [attributes], keywords
codeBlocker.WriteFileScopedNamespace(template.Namespace);
codeBlocker.WriteUsings(template.Usings);
codeBlocker.AddClasses(template.Classes);At file level the only attributes are global ones (assembly: / module:), and C# is strict about where they go. So any SourceFileTemplate that sets both Attributes and Usings renders a file that doesn't compile.
Reproduction
I rendered this with the library built from f86da28:
var f = new SourceFileTemplate { Namespace = ns };
f.Attributes.Add("assembly: CLSCompliant(true)");
f.Usings.Add("System");
f.Classes.Add(new ClassTemplate { Name = "C", Keywords = { "public" } });
codeBlocker.AddSourceFile(f);Namespace = "" gives:
[assembly: CLSCompliant(true)]
using System;
public class C
{
}That fails with error CS1529: A using clause must precede all other elements defined in the namespace except extern alias declarations.
Namespace = "N" gives:
[assembly: CLSCompliant(true)]
namespace N;
using System;
...That fails with error CS0246: The type or namespace name 'CLSCompliant' could not be found. The attribute sits outside the file-scoped namespace, so it can't see usings declared inside it. Moving the attribute below those usings doesn't help either: namespace N; using System; [assembly: CLSCompliant(true)] fails with CS1730.
I compiled all three layouts with the .NET 10 SDK to confirm the errors. The only layout where the attribute resolves through the template's own Usings is usings → global attributes → namespace.
Why it matters
The model is supposed to own the punctuation and ordering, so a caller never has to know C#'s placement rules. Today a generator that wants [assembly: InternalsVisibleTo(...)] or similar in a generated file has two choices: fully qualify every attribute name and also leave Usings empty when there is no namespace, or skip the template's Attributes and write the preamble by hand. Nothing documents either, and no test covers attributes on a SourceFileTemplate.
Suggested fix / acceptance criteria
- When
SourceFileTemplate.Attributesis non-empty,AddSourceFilewrites the usings first, then the global attributes, then the namespace. This is the only order in which both compile and the attributes see the usings. Without file-level attributes, the existing namespace-then-usings layout and the golden output stay exactly as they are. - Alternatively, if usings inside the namespace must stay unconditional, document on
SourceFileTemplatethat file-levelAttributesmust be fully qualified. Even then, emit usings before attributes whenNamespaceis empty, so that case stops being CS1529. - Regression tests: attributes plus usings, with and without a namespace. Each test compiles the output, or at least asserts the order.
- Optional:
Keywordson aSourceFileTemplateis written verbatim in front ofnamespace(public namespace N;), which can never be valid. Ignore it or reject it.
Activity
matt-edmondson commentedon Oct 18, 2026
Triage
SourceFileTemplatethat sets bothAttributesandUsingsrenders C# that doesn't compile (CS1529 with no namespace, CS0246 with one). The failure is loud rather than silent, and callers can work around it by fully qualifying attributes or writing the preamble by hand.Templates/SourceFileTemplate.cs, specifically theAddSourceFileemit order. Owner: @matt-edmondson[\n\t1,\n]initialiser drops to column 1 #132 and Write("") followed by NewLine() leaves a tab on the blank line and drops the next line to column 0 (the #115 fix misses empty writes) #126 (template output correctness), but the cause is separate.Attributesis non-empty, emit usings, then global attributes, then the namespace, and leave the golden output unchanged otherwise. Add compile or order tests with and without a namespace. Optionally rejectKeywordson a file template.Generated by Claude Code