Skip to content

Commit f48bc7b

Browse files
Copilotbcollamore
andcommitted
feat: Fix project.assets.json discovery and add .editorconfig debug logging control
- Fixed project search to use actual source file paths instead of VS install directory - Added .editorconfig option to control debug diagnostic logging (PH2155.enable_debug_logging) - Enhanced debugging with comprehensive search path reporting and exception handling - Default debug logging enabled for development phase - Updated documentation to explain debug logging configuration Co-authored-by: bcollamore <57269455+bcollamore@users.noreply.github.com>
1 parent aa0a62f commit f48bc7b

2 files changed

Lines changed: 133 additions & 46 deletions

File tree

Documentation/Diagnostics/PH2155.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,29 @@ The `Allowed.Licenses.txt` file supports:
129129
- Custom license names for commercial or proprietary packages
130130
- SPDX license identifiers
131131

132+
### Debug Logging Configuration
133+
134+
The analyzer provides detailed diagnostic logging to help troubleshoot issues with project.assets.json file discovery and license analysis. This is especially useful during development and deployment.
135+
136+
You can control debug logging via `.editorconfig`:
137+
138+
```ini
139+
# Enable debug logging (default: enabled for now since analyzer is not production ready)
140+
dotnet_code_quality.PH2155.enable_debug_logging = true
141+
142+
# Disable debug logging for production use
143+
dotnet_code_quality.PH2155.enable_debug_logging = false
144+
```
145+
146+
When enabled, the analyzer reports informational diagnostics about:
147+
- Project.assets.json file search paths and results
148+
- Source directory discovery from compilation
149+
- File system operations and any exceptions
150+
- Cache operations and license extraction results
151+
- Configuration status and search completion
152+
153+
**Note**: Debug logging is currently enabled by default since this analyzer is not yet production ready. This will be changed to disabled by default in future releases.
154+
132155
## Performance
133156

134157
The analyzer includes several performance optimizations:

Philips.CodeAnalysis.SecurityAnalyzers/LicenseAnalyzer.cs

Lines changed: 110 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,36 @@ public override void Initialize(AnalysisContext context)
8080
context.RegisterCompilationAction(AnalyzeProject);
8181
}
8282

83+
private static bool IsDiagnosticLoggingEnabled(CompilationAnalysisContext context)
84+
{
85+
// Check if diagnostic logging is enabled via .editorconfig
86+
// Default to enabled since we're not production ready yet
87+
if (!context.Compilation.SyntaxTrees.Any())
88+
{
89+
return true;
90+
}
91+
92+
SyntaxTree tree = context.Compilation.SyntaxTrees.First();
93+
AnalyzerConfigOptions analyzerConfigOptions = context.Options.AnalyzerConfigOptionsProvider.GetOptions(tree);
94+
95+
if (analyzerConfigOptions.TryGetValue("dotnet_code_quality.PH2155.enable_debug_logging", out var value))
96+
{
97+
return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase);
98+
}
99+
100+
// Default to enabled for now since we're far from production ready
101+
return true;
102+
}
103+
104+
private static void ReportDebugDiagnostic(CompilationAnalysisContext context, string message)
105+
{
106+
if (IsDiagnosticLoggingEnabled(context))
107+
{
108+
var diagnostic = Diagnostic.Create(InfoDiagnostic, Location.None, message);
109+
context.ReportDiagnostic(diagnostic);
110+
}
111+
}
112+
83113
private void AnalyzeProject(CompilationAnalysisContext context)
84114
{
85115
// Skip analysis for test projects to avoid noise during development
@@ -90,6 +120,10 @@ private void AnalyzeProject(CompilationAnalysisContext context)
90120

91121
try
92122
{
123+
// Report .editorconfig debug logging status
124+
var loggingEnabled = IsDiagnosticLoggingEnabled(context);
125+
ReportDebugDiagnostic(context, $"LicenseAnalyzer debug logging: {(loggingEnabled ? "enabled" : "disabled")} (configure via dotnet_code_quality.PH2155.enable_debug_logging=false)");
126+
93127
// Load custom allowed licenses from additional files
94128
HashSet<string> allowedLicenses = GetAllowedLicenses(context.Options.AdditionalFiles);
95129

@@ -104,8 +138,7 @@ private void AnalyzeProject(CompilationAnalysisContext context)
104138
catch (Exception ex)
105139
{
106140
// Report diagnostic about the error instead of silently failing
107-
var diagnostic = Diagnostic.Create(InfoDiagnostic, Location.None, $"Failed to analyze package licenses: {ex.Message}");
108-
context.ReportDiagnostic(diagnostic);
141+
ReportDebugDiagnostic(context, $"Failed to analyze package licenses: {ex.Message}");
109142
}
110143
}
111144

@@ -130,8 +163,7 @@ private void AnalyzePackagesFromAssetsFile(CompilationAnalysisContext context, H
130163
if (string.IsNullOrEmpty(assetsFilePath) || !File.Exists(assetsFilePath))
131164
{
132165
// Report diagnostic that project.assets.json was not found
133-
var diagnostic = Diagnostic.Create(InfoDiagnostic, Location.None, "Could not find project.assets.json file for license analysis");
134-
context.ReportDiagnostic(diagnostic);
166+
ReportDebugDiagnostic(context, "Could not find project.assets.json file for license analysis");
135167
return;
136168
}
137169

@@ -145,8 +177,7 @@ private void AnalyzePackagesFromAssetsFile(CompilationAnalysisContext context, H
145177
catch (Exception ex)
146178
{
147179
// Report diagnostic about parsing failure instead of silently failing
148-
var diagnostic = Diagnostic.Create(InfoDiagnostic, Location.None, $"Failed to parse project.assets.json: {ex.Message}");
149-
context.ReportDiagnostic(diagnostic);
180+
ReportDebugDiagnostic(context, $"Failed to parse project.assets.json: {ex.Message}");
150181
return;
151182
}
152183

@@ -189,80 +220,113 @@ private void AnalyzePackagesFromAssetsFile(CompilationAnalysisContext context, H
189220

190221
private static string TryFindAssetsFileFromSourcePaths(CompilationAnalysisContext context)
191222
{
192-
// For .NET Standard 2.0 compatibility, use alternative approaches to find project.assets.json
193-
// Try common locations relative to the current working directory
223+
// Search for project.assets.json using actual source file paths from the compilation
224+
// instead of Directory.GetCurrentDirectory() which points to VS install directory
225+
226+
var sourceDirectories = context.Compilation.SyntaxTrees
227+
.Where(tree => !string.IsNullOrEmpty(tree.FilePath))
228+
.Select(tree => Path.GetDirectoryName(tree.FilePath))
229+
.Where(dir => !string.IsNullOrEmpty(dir))
230+
.Distinct(StringComparer.OrdinalIgnoreCase)
231+
.ToList();
232+
233+
ReportDebugDiagnostic(context, $"Found {sourceDirectories.Count} unique source directories from compilation");
234+
235+
// Search in each source directory and its parent directories
236+
foreach (var sourceDir in sourceDirectories)
237+
{
238+
ReportDebugDiagnostic(context, $"Searching from source directory: {sourceDir}");
239+
240+
var foundPath = SearchForAssetsFile(context, sourceDir);
241+
if (foundPath != null)
242+
{
243+
ReportDebugDiagnostic(context, $"Found {ProjectAssetsFileName} at: {foundPath}");
244+
return foundPath;
245+
}
246+
}
247+
248+
// Fallback to current directory search if source paths didn't work
194249
var currentDir = Directory.GetCurrentDirectory();
250+
ReportDebugDiagnostic(context, $"Source directory search failed, falling back to current directory: {currentDir}");
195251

196-
// Report debug information about search starting point
197-
var diagnostic = Diagnostic.Create(InfoDiagnostic, Location.None, $"Searching for {ProjectAssetsFileName} starting from directory: {currentDir}");
198-
context.ReportDiagnostic(diagnostic);
252+
var fallbackPath = SearchForAssetsFile(context, currentDir);
253+
if (fallbackPath != null)
254+
{
255+
ReportDebugDiagnostic(context, $"Found {ProjectAssetsFileName} via fallback at: {fallbackPath}");
256+
return fallbackPath;
257+
}
199258

200-
// Look for project.assets.json in common locations
259+
ReportDebugDiagnostic(context, $"Exhaustive search completed, {ProjectAssetsFileName} not found");
260+
return null;
261+
}
262+
263+
private static string SearchForAssetsFile(CompilationAnalysisContext context, string startDirectory)
264+
{
265+
// Look for project.assets.json in common locations relative to the start directory
201266
var possiblePaths = new[]
202267
{
203-
Path.Combine(currentDir, "obj", ProjectAssetsFileName),
204-
Path.Combine(currentDir, "..", "obj", ProjectAssetsFileName),
205-
Path.Combine(currentDir, "..", "..", "obj", ProjectAssetsFileName)
268+
Path.Combine(startDirectory, "obj", ProjectAssetsFileName),
269+
Path.Combine(startDirectory, "..", "obj", ProjectAssetsFileName),
270+
Path.Combine(startDirectory, "..", "..", "obj", ProjectAssetsFileName)
206271
};
207272

208-
// Report each path being checked
273+
// Check each possible path
209274
foreach (var path in possiblePaths)
210275
{
211-
var exists = File.Exists(path);
212-
var diagnostic2 = Diagnostic.Create(InfoDiagnostic, Location.None, $"Checking path: {path} - Exists: {exists}");
213-
context.ReportDiagnostic(diagnostic2);
214-
}
276+
try
277+
{
278+
var exists = File.Exists(path);
279+
ReportDebugDiagnostic(context, $"Checking path: {path} - Exists: {exists}");
215280

216-
var foundPath = possiblePaths.Where(File.Exists).FirstOrDefault();
217-
if (foundPath != null)
218-
{
219-
var diagnostic3 = Diagnostic.Create(InfoDiagnostic, Location.None, $"Found {ProjectAssetsFileName} at: {foundPath}");
220-
context.ReportDiagnostic(diagnostic3);
221-
return foundPath;
281+
if (exists)
282+
{
283+
return path;
284+
}
285+
}
286+
catch (Exception ex)
287+
{
288+
ReportDebugDiagnostic(context, $"Exception checking path {path}: {ex.Message}");
289+
}
222290
}
223291

224-
// If not found in common locations, search in current directory tree
292+
// If not found in common locations, search up the directory tree
225293
try
226294
{
227-
var diagnostic4 = Diagnostic.Create(InfoDiagnostic, Location.None, "Common paths failed, searching directory tree...");
228-
context.ReportDiagnostic(diagnostic4);
229-
230-
var searchDir = currentDir;
295+
var searchDir = startDirectory;
231296
for (var i = 0; i < 5; i++) // Limit search depth
232297
{
233298
var assetsPath = Path.Combine(searchDir, "obj", ProjectAssetsFileName);
234-
var exists = File.Exists(assetsPath);
235299

236-
var diagnostic5 = Diagnostic.Create(InfoDiagnostic, Location.None, $"Tree search depth {i}: checking {assetsPath} - Exists: {exists}");
237-
context.ReportDiagnostic(diagnostic5);
300+
try
301+
{
302+
var exists = File.Exists(assetsPath);
303+
ReportDebugDiagnostic(context, $"Tree search depth {i}: checking {assetsPath} - Exists: {exists}");
238304

239-
if (exists)
305+
if (exists)
306+
{
307+
return assetsPath;
308+
}
309+
}
310+
catch (Exception ex)
240311
{
241-
var diagnostic6 = Diagnostic.Create(InfoDiagnostic, Location.None, $"Found {ProjectAssetsFileName} via tree search at: {assetsPath}");
242-
context.ReportDiagnostic(diagnostic6);
243-
return assetsPath;
312+
ReportDebugDiagnostic(context, $"Exception during tree search at {assetsPath}: {ex.Message}");
244313
}
245314

246315
DirectoryInfo parentDir = Directory.GetParent(searchDir);
247316
if (parentDir == null)
248317
{
249-
var diagnostic7 = Diagnostic.Create(InfoDiagnostic, Location.None, $"Reached root directory at depth {i}, stopping search");
250-
context.ReportDiagnostic(diagnostic7);
318+
ReportDebugDiagnostic(context, $"Reached root directory at depth {i}, stopping search");
251319
break;
252320
}
321+
253322
searchDir = parentDir.FullName;
254323
}
255324
}
256325
catch (Exception ex)
257326
{
258-
// Report diagnostic about directory traversal failure
259-
var diagnostic8 = Diagnostic.Create(InfoDiagnostic, Location.None, $"Directory traversal failed: {ex.Message}");
260-
context.ReportDiagnostic(diagnostic8);
261-
return null;
327+
ReportDebugDiagnostic(context, $"Exception during directory tree search: {ex.Message}");
262328
}
263329

264-
var diagnostic9 = Diagnostic.Create(InfoDiagnostic, Location.None, $"{ProjectAssetsFileName} not found after exhaustive search");
265-
context.ReportDiagnostic(diagnostic9);
266330
return null;
267331
}
268332

0 commit comments

Comments
 (0)