/// <summary> /// Returns True if the text has incorrect spelling. /// </summary> /// <param name="element"> /// The element containing the text we're checking. /// </param> /// <param name="text"> /// The text to check. /// </param> /// <param name="spellingError"> /// Returns a comma separated list of words encountered as spelling errors. /// </param> /// <returns> /// True if the text contains an incorrect spelling. /// </returns> private static bool TextContainsIncorectSpelling(CsElement element, string text, out string spellingError) { NamingService namingService = NamingService.GetNamingService(element.Document.SourceCode.Project.Culture); spellingError = string.Empty; if (namingService.SupportsSpelling) { WordParser parser = new WordParser(text, WordParserOptions.SplitCompoundWords); if (parser.PeekWord() != null) { ICollection <string> recognizedWords = element.Document.SourceCode.Project.RecognizedWords; string word = parser.NextWord(); do { // Ignore words starting and ending with '$'. // Ignore if in our recognized words list or correct spelling if ((word.StartsWith("$") && word.EndsWith("$")) || (recognizedWords.Contains(word) || IsSpelledCorrectly(namingService, word))) { continue; } if (!string.IsNullOrEmpty(spellingError)) { spellingError += ", "; } spellingError += word; }while ((word = parser.NextWord()) != null); } } return(!string.IsNullOrEmpty(spellingError)); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterSymbolAction(context => { var eventName = context.Symbol.Name; if (!eventName.StartsWith(BeforeKeyword, StringComparison.Ordinal) && !eventName.StartsWith(AfterKeyword, StringComparison.Ordinal)) { return; } var wordParse = new WordParser(eventName, WordParserOptions.SplitCompoundWords); if (wordParse.NextWord() is string firstWord && s_invalidPrefixes.Contains(firstWord) && wordParse.NextWord() is string) // Do not report if this is the only word { context.ReportDiagnostic(context.Symbol.CreateDiagnostic(Rule)); } }, SymbolKind.Event); }
/// <summary> /// Returns True if the text has incorrect spelling. /// </summary> /// <param name="element"> /// The element containing the text we're checking. /// </param> /// <param name="text"> /// The text to check. /// </param> /// <param name="spellingError"> /// Returns a comma separated list of words encountered as spelling errors. /// </param> /// <returns> /// True if the text contains an incorrect spelling. /// </returns> private static bool TextContainsIncorectSpelling(CsElement element, string text, out string spellingError) { NamingService namingService = NamingService.GetNamingService(element.Document.SourceCode.Project.Culture); spellingError = string.Empty; if (namingService.SupportsSpelling) { ICollection <string> recognizedWords = element.Document.SourceCode.Project.RecognizedWords; WordParser parser = new WordParser(text, WordParserOptions.SplitCompoundWords, recognizedWords); if (parser.PeekWord() != null) { string word = parser.NextWord(); do { // Ignore words starting and ending with '$'. // Ignore if in our recognized words list or correct spelling string hint = null; if ((word.StartsWith("$") && word.EndsWith("$")) || (recognizedWords.Contains(word) || IsSpelledCorrectly(namingService, word, out hint))) { continue; } // If we already have a spelling error for this element, add a comma to separate them. if (!string.IsNullOrEmpty(spellingError)) { spellingError += ", "; } spellingError += "'" + word + "'"; // Append a hint message to this spelling error if we have one. if (hint != null && hint.Trim().Length > 0) { spellingError += " " + hint; } }while ((word = parser.NextWord()) != null); } } return(!string.IsNullOrEmpty(spellingError)); }
private static bool HasEventLikeName(IMethodSymbol method) { WordParser parser = new WordParser(method.Name, WordParserOptions.SplitCompoundWords); string word = parser.NextWord(); // Check for 'FireXXX', 'RaiseXXX' if (string.Equals(word, "fire", StringComparison.OrdinalIgnoreCase) || string.Equals(word, "raise", StringComparison.OrdinalIgnoreCase)) { return(true); } // Check for 'AddOnXXX', 'RemoveOnXXX' if (string.Equals(word, "add", StringComparison.OrdinalIgnoreCase) || string.Equals(word, "remove", StringComparison.OrdinalIgnoreCase)) { return(string.Equals(parser.NextWord(), "on", StringComparison.OrdinalIgnoreCase)); } return(false); }
private static bool HasCorrectPrefix(ISymbol symbol, char prefix) { WordParser parser = new WordParser(symbol.Name, WordParserOptions.SplitCompoundWords, prefix); string firstWord = parser.NextWord(); if (firstWord == null || firstWord.Length > 1) { return(false); } return(firstWord[0] == prefix); }
public static bool SymbolNameContainsUriWords(this ISymbol symbol, CancellationToken cancellationToken) { if (symbol.Name == null || !symbol.SymbolNameContainsUriWordSubstring(cancellationToken)) { // quick check failed return false; } string word; var parser = new WordParser(symbol.Name, WordParserOptions.SplitCompoundWords); while ((word = parser.NextWord()) != null) { if (s_uriWords.Contains(word)) { return true; } } return false; }
public static bool SymbolNameContainsUriWords(this ISymbol symbol, CancellationToken cancellationToken) { if (symbol.Name == null || !symbol.SymbolNameContainsUriWordSubstring(cancellationToken)) { // quick check failed return(false); } string word; var parser = new WordParser(symbol.Name, WordParserOptions.SplitCompoundWords); while ((word = parser.NextWord()) != null) { if (s_uriWords.Contains(word)) { return(true); } } return(false); }
private IEnumerable <IParameterSymbol> GetStringParametersThatContainsUriWords(IEnumerable <IParameterSymbol> stringParameters, CancellationToken cancellationToken) { foreach (IParameterSymbol parameter in stringParameters) { if (parameter.Name == null || !CheckStringParameterContainsUriWords(parameter, cancellationToken)) { // quick check failed continue; } string word; var parser = new WordParser(parameter.Name, WordParserOptions.SplitCompoundWords); while ((word = parser.NextWord()) != null) { if (s_uriWords.Contains(word)) { yield return(parameter); break; } } } }
private static void OnCompilationStart(CompilationStartAnalysisContext compilationStartContext) { var dictionaries = ReadDictionaries(); var projectDictionary = CodeAnalysisDictionary.CreateFromDictionaries(dictionaries.Concat(s_mainDictionary)); compilationStartContext.RegisterOperationAction(AnalyzeVariable, OperationKind.VariableDeclarator); compilationStartContext.RegisterCompilationEndAction(AnalyzeAssembly); compilationStartContext.RegisterSymbolAction( AnalyzeSymbol, SymbolKind.Namespace, SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Property, SymbolKind.Event, SymbolKind.Field, SymbolKind.Parameter); IEnumerable <CodeAnalysisDictionary> ReadDictionaries() { var fileProvider = AdditionalFileProvider.FromOptions(compilationStartContext.Options); return(fileProvider.GetMatchingFiles(@"(?:dictionary|custom).*?\.(?:xml|dic)$") .Select(CreateDictionaryFromAdditionalText) .Where(x => x != null) .ToList()); CodeAnalysisDictionary CreateDictionaryFromAdditionalText(AdditionalText additionalFile) { var text = additionalFile.GetText(compilationStartContext.CancellationToken); var isXml = additionalFile.Path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase); var provider = isXml ? s_xmlDictionaryProvider : s_dicDictionaryProvider; if (!compilationStartContext.TryGetValue(text, provider, out var dictionary)) { try { // Annoyingly (and expectedly), TryGetValue swallows the parsing exception, // so we have to parse again to get it. var unused = isXml ? ParseXmlDictionary(text) : ParseDicDictionary(text); ReportFileParseDiagnostic(additionalFile.Path, "Unknown error"); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { ReportFileParseDiagnostic(additionalFile.Path, ex.Message); } } return(dictionary); } void ReportFileParseDiagnostic(string filePath, string message) { var diagnostic = Diagnostic.Create(FileParseRule, Location.None, filePath, message); compilationStartContext.RegisterCompilationEndAction(x => x.ReportDiagnostic(diagnostic)); } } void AnalyzeVariable(OperationAnalysisContext operationContext) { var variableOperation = (IVariableDeclaratorOperation)operationContext.Operation; var variable = variableOperation.Symbol; ReportDiagnosticsForSymbol(variable, variable.Name, operationContext.ReportDiagnostic, checkForUnmeaningful: false); } void AnalyzeAssembly(CompilationAnalysisContext context) { var assembly = context.Compilation.Assembly; ReportDiagnosticsForSymbol(assembly, assembly.Name, context.ReportDiagnostic); } void AnalyzeSymbol(SymbolAnalysisContext symbolContext) { var typeParameterDiagnostics = Enumerable.Empty <Diagnostic>(); ISymbol symbol = symbolContext.Symbol; if (symbol.IsOverride) { return; } var symbolName = symbol.Name; switch (symbol) { case IFieldSymbol: symbolName = RemovePrefixIfPresent('_', symbolName); break; case IMethodSymbol method: switch (method.MethodKind) { case MethodKind.PropertyGet: case MethodKind.PropertySet: return; case MethodKind.Constructor: case MethodKind.StaticConstructor: symbolName = symbol.ContainingType.Name; break; } foreach (var typeParameter in method.TypeParameters) { ReportDiagnosticsForSymbol(typeParameter, RemovePrefixIfPresent('T', typeParameter.Name), symbolContext.ReportDiagnostic); } break; case INamedTypeSymbol type: if (type.TypeKind == TypeKind.Interface) { symbolName = RemovePrefixIfPresent('I', symbolName); } foreach (var typeParameter in type.TypeParameters) { ReportDiagnosticsForSymbol(typeParameter, RemovePrefixIfPresent('T', typeParameter.Name), symbolContext.ReportDiagnostic); } break; } ReportDiagnosticsForSymbol(symbol, symbolName, symbolContext.ReportDiagnostic); } void ReportDiagnosticsForSymbol(ISymbol symbol, string symbolName, Action <Diagnostic> reportDiagnostic, bool checkForUnmeaningful = true) { foreach (var misspelledWord in GetMisspelledWords(symbolName)) { reportDiagnostic(GetMisspelledWordDiagnostic(symbol, misspelledWord)); } if (checkForUnmeaningful && symbolName.Length == 1) { reportDiagnostic(GetUnmeaningfulIdentifierDiagnostic(symbol, symbolName)); } } IEnumerable <string> GetMisspelledWords(string symbolName) { var parser = new WordParser(symbolName, WordParserOptions.SplitCompoundWords); string?word; while ((word = parser.NextWord()) != null) { if (!IsWordAcronym(word) && !IsWordNumeric(word) && !IsWordSpelledCorrectly(word)) { yield return(word); } } }
private void OnCompilationStart(CompilationStartAnalysisContext compilationStartContext) { if (IsRunningInProduction && InDebugMode) { System.Diagnostics.Debugger.Launch(); } var projectDictionary = _mainDictionary.Clone(); var dictionaries = ReadDictionaries(); if (dictionaries.Any()) { var aggregatedDictionary = dictionaries.Aggregate((x, y) => x.CombineWith(y)); projectDictionary = projectDictionary.CombineWith(aggregatedDictionary); } compilationStartContext.RegisterOperationAction(AnalyzeVariable, OperationKind.VariableDeclarator); compilationStartContext.RegisterCompilationEndAction(AnalyzeAssembly); compilationStartContext.RegisterSymbolAction( AnalyzeSymbol, SymbolKind.Namespace, SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Property, SymbolKind.Event, SymbolKind.Field, SymbolKind.Parameter); IEnumerable <CodeAnalysisDictionary> ReadDictionaries() { var fileProvider = AdditionalFileProvider.FromOptions(compilationStartContext.Options); return(fileProvider.GetMatchingFiles(@"(?:dictionary|custom).*?\.(?:xml|dic)$") .Select(CreateDictionaryFromAdditionalText) .Where(x => x != null)); CodeAnalysisDictionary CreateDictionaryFromAdditionalText(AdditionalText additionalFile) { var text = additionalFile.GetText(compilationStartContext.CancellationToken); var isXml = additionalFile.Path.EndsWith("xml", StringComparison.OrdinalIgnoreCase); var provider = isXml ? _xmlDictionaryProvider : _dicDictionaryProvider; if (!compilationStartContext.TryGetValue(text, provider, out var dictionary)) { try { // Annoyingly (and expectedly), TryGetValue swallows the parsing exception, // so we have to parse again to get it. var unused = isXml ? ParseXmlDictionary(text) : ParseDicDictionary(text); ReportFileParseDiagnostic(additionalFile.Path, "Unknown error"); } catch (Exception ex) { ReportFileParseDiagnostic(additionalFile.Path, ex.Message); } } return(dictionary); } void ReportFileParseDiagnostic(string filePath, string message) { var diagnostic = Diagnostic.Create(FileParseRule, Location.None, filePath, message); compilationStartContext.RegisterCompilationEndAction(x => x.ReportDiagnostic(diagnostic)); } } void AnalyzeVariable(OperationAnalysisContext operationContext) { var variableOperation = (IVariableDeclaratorOperation)operationContext.Operation; var variable = variableOperation.Symbol; var diagnostics = GetDiagnosticsForSymbol(variable, variable.Name, checkForUnmeaningful: false); foreach (var diagnostic in diagnostics) { operationContext.ReportDiagnostic(diagnostic); } } void AnalyzeAssembly(CompilationAnalysisContext context) { var assembly = context.Compilation.Assembly; var diagnostics = GetDiagnosticsForSymbol(assembly, assembly.Name); foreach (var diagnostic in diagnostics) { context.ReportDiagnostic(diagnostic); } } void AnalyzeSymbol(SymbolAnalysisContext symbolContext) { var typeParameterDiagnostics = Enumerable.Empty <Diagnostic>(); ISymbol symbol = symbolContext.Symbol; if (symbol.IsOverride) { return; } var symbolName = symbol.Name; switch (symbol) { case IFieldSymbol field: symbolName = RemovePrefixIfPresent("_", symbolName); break; case IMethodSymbol method: switch (method.MethodKind) { case MethodKind.PropertyGet: case MethodKind.PropertySet: return; case MethodKind.Constructor: case MethodKind.StaticConstructor: symbolName = symbol.ContainingType.Name; break; } foreach (var typeParameter in method.TypeParameters) { typeParameterDiagnostics = GetDiagnosticsForSymbol(typeParameter, RemovePrefixIfPresent("T", typeParameter.Name)); } break; case INamedTypeSymbol type: if (type.TypeKind == TypeKind.Interface) { symbolName = RemovePrefixIfPresent("I", symbolName); } foreach (var typeParameter in type.TypeParameters) { typeParameterDiagnostics = GetDiagnosticsForSymbol(typeParameter, RemovePrefixIfPresent("T", typeParameter.Name)); } break; } var diagnostics = GetDiagnosticsForSymbol(symbol, symbolName); var allDiagnostics = typeParameterDiagnostics.Concat(diagnostics); foreach (var diagnostic in allDiagnostics) { symbolContext.ReportDiagnostic(diagnostic); } } IEnumerable <Diagnostic> GetDiagnosticsForSymbol(ISymbol symbol, string symbolName, bool checkForUnmeaningful = true) { var diagnostics = new List <Diagnostic>(); if (checkForUnmeaningful && symbolName.Length == 1) { // diagnostics.AddRange(GetUnmeaningfulIdentifierDiagnostics(symbol, symbolName)); } else { foreach (var misspelledWord in GetMisspelledWords(symbolName)) { diagnostics.AddRange(GetMisspelledWordDiagnostics(symbol, misspelledWord)); } } return(diagnostics); } IEnumerable <string> GetMisspelledWords(string symbolName) { var parser = new WordParser(symbolName, WordParserOptions.SplitCompoundWords); if (parser.PeekWord() != null) { var word = parser.NextWord(); do { if (IsWordAcronym(word) || IsWordNumeric(word) || IsWordSpelledCorrectly(word)) { continue; } yield return(word); }while ((word = parser.NextWord()) != null); } } bool IsWordAcronym(string word) => word.All(char.IsUpper); bool IsWordNumeric(string word) => char.IsDigit(word[0]); bool IsWordSpelledCorrectly(string word) => !projectDictionary.UnrecognizedWords.Contains(word) && projectDictionary.RecognizedWords.Contains(word); }