From 20bf4ad759f967293eac78043078b15390598104 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Sat, 28 Sep 2019 16:21:58 +0300 Subject: [PATCH] [modelica] Initial implementation of Modelica support for PMD --- README.md | 4 +- pmd-dist/pom.xml | 5 + .../pmd/it/BinaryDistributionIT.java | 4 +- pmd-modelica/etc/grammar/Modelica.jjt | 989 ++++++++++++++++++ pmd-modelica/pom.xml | 83 ++ pmd-modelica/src/main/ant/alljavacc.xml | 157 +++ .../sourceforge/pmd/cpd/ModelicaLanguage.java | 13 + .../pmd/cpd/ModelicaTokenizer.java | 67 ++ .../pmd/lang/modelica/ModelicaHandler.java | 49 + .../lang/modelica/ModelicaLanguageModule.java | 18 + .../pmd/lang/modelica/ModelicaParser.java | 45 + .../lang/modelica/ModelicaTokenManager.java | 31 + .../lang/modelica/ast/ASTClassDefinition.java | 78 ++ .../modelica/ast/ASTComponentReference.java | 77 ++ .../modelica/ast/ASTDerClassSpecifier.java | 20 + .../pmd/lang/modelica/ast/ASTElementList.java | 30 + .../ASTEnumerationShortClassSpecifier.java | 20 + .../ast/ASTExtendingLongClassSpecifier.java | 29 + .../ASTMultipleDefinitionImportClause.java | 63 ++ .../pmd/lang/modelica/ast/ASTName.java | 85 ++ .../modelica/ast/ASTRenamingImportClause.java | 54 + .../ast/ASTSimpleLongClassSpecifier.java | 28 + .../ast/ASTSimpleShortClassSpecifier.java | 33 + .../ast/ASTSingleDefinitionImportClause.java | 54 + .../modelica/ast/ASTStoredDefinition.java | 55 + .../ast/ASTUnqualifiedImportClause.java | 54 + .../lang/modelica/ast/ASTWithinClause.java | 32 + .../AbstractModelicaClassSpecifierNode.java | 70 ++ .../ast/AbstractModelicaImportClause.java | 68 ++ .../modelica/ast/AbstractModelicaNode.java | 93 ++ .../modelica/ast/AbstractModelicaRule.java | 753 +++++++++++++ .../pmd/lang/modelica/ast/Helper.java | 28 + .../modelica/ast/InternalModelicaNodeApi.java | 32 + .../ast/ModelicaClassSpecifierNode.java | 12 + .../modelica/ast/ModelicaImportClause.java | 8 + .../pmd/lang/modelica/ast/ModelicaNode.java | 33 + .../ast/ModelicaParserVisitorAdapter.java | 729 +++++++++++++ .../modelica/ast/ResolvableModelicaNode.java | 17 + .../pmd/lang/modelica/ast/Visibility.java | 12 + .../resolver/AbstractModelicaDeclaration.java | 19 + .../resolver/AbstractModelicaScope.java | 90 ++ .../lang/modelica/resolver/CompositeName.java | 86 ++ .../resolver/InternalModelicaResolverApi.java | 26 + .../resolver/ModelicaBuiltinType.java | 59 ++ .../resolver/ModelicaClassDeclaration.java | 279 +++++ .../modelica/resolver/ModelicaClassScope.java | 44 + .../resolver/ModelicaClassSpecialization.java | 47 + .../modelica/resolver/ModelicaClassType.java | 35 + .../ModelicaComponentDeclaration.java | 218 ++++ .../resolver/ModelicaDeclaration.java | 20 + .../lang/modelica/resolver/ModelicaScope.java | 34 + .../resolver/ModelicaSourceFileScope.java | 79 ++ .../resolver/ModelicaSymbolFacade.java | 14 + .../lang/modelica/resolver/ModelicaType.java | 20 + .../modelica/resolver/ResolutionContext.java | 140 +++ .../modelica/resolver/ResolutionResult.java | 66 ++ .../modelica/resolver/ResolutionState.java | 35 + .../modelica/resolver/ResolvableEntity.java | 15 + .../pmd/lang/modelica/resolver/RootScope.java | 61 ++ .../resolver/ScopeAndDeclarationFinder.java | 75 ++ .../resolver/SubcomponentResolver.java | 23 + .../pmd/lang/modelica/resolver/Watchdog.java | 29 + .../rule/AmbiguousResolutionRule.java | 27 + .../rule/ConnectUsingNonConnector.java | 51 + .../rule/ModelicaRuleChainVisitor.java | 42 + .../rule/ModelicaRuleViolationFactory.java | 32 + .../services/net.sourceforge.pmd.cpd.Language | 1 + .../net.sourceforge.pmd.lang.Language | 1 + .../category/modelica/bestpractices.xml | 53 + .../category/modelica/categories.properties | 5 + .../sourceforge/pmd/LanguageVersionTest.java | 29 + .../sourceforge/pmd/RuleSetFactoryTest.java | 9 + .../pmd/lang/modelica/ModelicaLoader.java | 42 + .../pmd/lang/modelica/ModelicaParserTest.java | 24 + .../resolver/ModelicaResolverTest.java | 333 ++++++ .../AmbiguousResolutionTest.java | 11 + .../ConnectUsingNonConnectorTest.java | 11 + .../pmd/lang/modelica/ParserTestGraphical.mo | 15 + .../pmd/lang/modelica/ParserTestTextual.mo | 9 + .../bestpractices/xml/AmbiguousResolution.xml | 86 ++ .../xml/ConnectUsingNonConnector.xml | 52 + .../src/main/resources/rule-tests_1_0_0.xsd | 2 +- pom.xml | 5 +- 83 files changed, 6379 insertions(+), 7 deletions(-) create mode 100644 pmd-modelica/etc/grammar/Modelica.jjt create mode 100644 pmd-modelica/pom.xml create mode 100644 pmd-modelica/src/main/ant/alljavacc.xml create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaParser.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaTokenManager.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTClassDefinition.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTComponentReference.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTDerClassSpecifier.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTElementList.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTEnumerationShortClassSpecifier.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTExtendingLongClassSpecifier.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTMultipleDefinitionImportClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTName.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTRenamingImportClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleLongClassSpecifier.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleShortClassSpecifier.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSingleDefinitionImportClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTStoredDefinition.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTUnqualifiedImportClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTWithinClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaClassSpecifierNode.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaImportClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaNode.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaRule.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Helper.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/InternalModelicaNodeApi.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaClassSpecifierNode.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaImportClause.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaNode.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParserVisitorAdapter.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ResolvableModelicaNode.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Visibility.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaDeclaration.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaScope.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/CompositeName.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/InternalModelicaResolverApi.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaBuiltinType.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassDeclaration.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassScope.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassSpecialization.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassType.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaComponentDeclaration.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaDeclaration.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaScope.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSourceFileScope.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaType.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionContext.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionResult.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionState.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolvableEntity.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/RootScope.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/SubcomponentResolver.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/Watchdog.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AmbiguousResolutionRule.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ConnectUsingNonConnector.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleChainVisitor.java create mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleViolationFactory.java create mode 100644 pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language create mode 100644 pmd-modelica/src/main/resources/category/modelica/bestpractices.xml create mode 100644 pmd-modelica/src/main/resources/category/modelica/categories.properties create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaLoader.java create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParserTest.java create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaResolverTest.java create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/AmbiguousResolutionTest.java create mode 100644 pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/ConnectUsingNonConnectorTest.java create mode 100644 pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestGraphical.mo create mode 100644 pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestTextual.mo create mode 100644 pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/AmbiguousResolution.xml create mode 100644 pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/ConnectUsingNonConnector.xml diff --git a/README.md b/README.md index 51cc828da9..e89fa6db57 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ **PMD** is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It supports Java, JavaScript, Salesforce.com Apex and Visualforce, -PLSQL, Apache Velocity, XML, XSL, Scala. +Modelica, PLSQL, Apache Velocity, XML, XSL, Scala. Additionally it includes **CPD**, the copy-paste-detector. CPD finds duplicated code in -C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, +C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift and Visualforce. ## Source and Documentation diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 41a671e162..d6eb5253c5 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -162,6 +162,11 @@ pmd-matlab ${project.version} + + net.sourceforge.pmd + pmd-modelica + ${project.version} + net.sourceforge.pmd pmd-perl diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index 8974f28c8d..b0b4df99d4 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -66,7 +66,7 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { result = PMDExecutor.runPMD(tempDir, "-h"); // note: the language "text" is provided by pmd-designer - result.assertExecutionResult(0, "apex, ecmascript, java, jsp, plsql, pom, scala, text, vf, vm, wsdl, xml, xsl"); + result.assertExecutionResult(0, "apex, ecmascript, java, jsp, modelica, plsql, pom, scala, text, vf, vm, wsdl, xml, xsl"); result = PMDExecutor.runPMDRules(tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml"); result.assertExecutionResult(4, "", "JumbledIncrementer.java:8:"); @@ -82,7 +82,7 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { ExecutionResult result; result = CpdExecutor.runCpd(tempDir, "-h"); - result.assertExecutionResult(0, "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, lua, matlab, objectivec, perl, php, plsql, python, ruby, scala, swift, vf]"); + result.assertExecutionResult(0, "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, vf]"); result = CpdExecutor.runCpd(tempDir, "--minimum-tokens", "10", "--format", "text", "--files", srcDir); result.assertExecutionResult(4, "Found a 10 line (55 tokens) duplication in the following files:"); diff --git a/pmd-modelica/etc/grammar/Modelica.jjt b/pmd-modelica/etc/grammar/Modelica.jjt new file mode 100644 index 0000000000..33a786817d --- /dev/null +++ b/pmd-modelica/etc/grammar/Modelica.jjt @@ -0,0 +1,989 @@ +/* + BSD-style license; for more info see http://pmd.sourceforge.net/license.html + + Based on Antlr4 Modelica grammar downloaded from https://github.com/antlr/grammars-v4 + License of the original modelica.g4: + +[The "BSD licence"] +Copyright (c) 2012 Tom Everett +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products +derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +options { + CACHE_TOKENS=true; + STATIC = false; + USER_CHAR_STREAM = true; + JDK_VERSION = "1.5"; + + MULTI = true; + VISITOR = true; + NODE_USES_PARSER = true; + TRACK_TOKENS = true; + NODE_PACKAGE="net.sourceforge.pmd.lang.modelica.ast"; + + UNICODE_INPUT = true; +} + +PARSER_BEGIN(ModelicaParser) +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.TokenMgrError; + +public class ModelicaParser { + +} + +PARSER_END(ModelicaParser) + +TOKEN: { + +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| + +| +| +| +| +| + +| +| " > +| =" > +| +| " > + +| +| +| +| +| +| +| +| +| +| +} + +TOKEN: { + ( | )*) | > +| <#Q_IDENT: "\'" ( | )+ "\'"> +| <#S_CHAR: ~["\"", "\\"] > +| <#NONDIGIT: [ "_", "a" - "z", "A" - "Z" ]> +| | )* "\""> +| <#Q_CHAR: | | [ "!", "#", "$", "%", "&", "(", ")", "*", "+", ",", "-", ".", "/", ":", ";", "<", ">", "=", "?", "@", "[", "]", "^", "{", "}", "|", "~" ] > +| <#S_ESCAPE: "\\" [ "\u2019", "\'", "\"", "?", "\\", "a", "b", "f", "n", "r", "t", "v" ] > +| <#DIGIT: ["0" - "9"]> +| <#UNSIGNED_INTEGER: ()+ > +| ("." ()?)? (["e", "E"] (["+", "-"])? )?> +} + +SKIP: { + +| +} + + +// Copyed from cpp.jj +MORE: +{ "/*" : IN_MULTI_LINE_COMMENT } + + SPECIAL_TOKEN: +{ : DEFAULT } + + MORE: +{ < ~[] > } + + +ASTStoredDefinition StoredDefinition(): {} +{ + ( {jjtThis.markHasBOM(); } )? + (( (Name())? ) #WithinClause)* + (()? ClassDefinition() )* + + { return jjtThis; } +} + +void ClassDefinition(): {} +{ + ( #EncapsulatedClause)? + ClassPrefixes() + ClassSpecifier() +} + +void ClassSpecifier(): {} +{ + LOOKAHEAD(2) LongClassSpecifier() + | LOOKAHEAD(3) DerClassSpecifier() + | ShortClassSpecifier() +} + +void ClassPrefixes(): {} +{ + ( #PartialClause)? + ( + #ClassClause + | #ModelClause + | #RecordClause + | LOOKAHEAD(2) ( #OperatorRecordClause) + | #BlockClause + | LOOKAHEAD(2) ( #ExpandableConnectorClause) + | #ConnectorClause + | #TypeClause + | #PackageClause + | LOOKAHEAD(3) (( #PureClause | #ImpureClause)? ( #OperatorClause)? ) #FunctionClause + | #Operator + ) +} + +void LongClassSpecifier() #void: {} +{ + ( + SimpleName() + StringComment() + Composition() + + SimpleName() + ) #SimpleLongClassSpecifier + | ( + + SimpleName() + (ClassModification())? + StringComment() + Composition() + + SimpleName() + ) #ExtendingLongClassSpecifier +} + +void ShortClassSpecifier() #void: {} +{ + LOOKAHEAD(3) ( + SimpleName() + + BasePrefix() + Name() + (ArraySubscripts())? + (ClassModification())? + Comment() + ) #SimpleShortClassSpecifier + | ( + SimpleName() + + + + ( + + | (EnumList())? + ) + + Comment() + ) #EnumerationShortClassSpecifier +} + +void DerClassSpecifier(): {} +{ + SimpleName() + + + ( + + Name() + + + ( + + SimpleName() + )* + + ) #DerClause + Comment() +} + +void BasePrefix(): {} +{ + TypePrefix() +} + +void EnumList(): {} +{ + EnumerationLiteral() + ( + + EnumerationLiteral() + )* +} + +void EnumerationLiteral(): {} +{ + SimpleName() + Comment() +} + +void Composition(): {} +{ + ElementList(Visibility.UNSPEC) + ( + ( ElementList(Visibility.PUBLIC)) + | ( ElementList(Visibility.PROTECTED)) + | LOOKAHEAD(2) EquationSection() + | LOOKAHEAD(2) AlgorithmSection() + )* + (( + + (LanguageSpecification())? + (ExternalFunctionCall())? + (Annotation())? + + ) #ExternalClause)? + ( + Annotation() + + )? +} + +void LanguageSpecification(): +{ + Token t; +} +{ + t = + { jjtThis.setImage(t.image); } +} + +void ExternalFunctionCall(): {} +{ + ( + LOOKAHEAD(2) ComponentReference() + + )? + SimpleName() + + (ExpressionList())? + + +} + +void ElementList(Visibility v): {} +{ + { jjtThis.setVisibility(v); } + ( + Element() + + )* +} + +void Element() #void: {} +{ + ImportClause() + | ExtendsClause() + | ( + ( #RedeclareClause)? + ( #FinalClause)? + ( #InnerClause)? + ( #OuterClause)? + ( + (ClassDefinition() | ComponentClause()) + | ( (ClassDefinition() | ComponentClause()) (ConstrainingClause() Comment())?) #ReplaceableClause + ) + ) #RegularElement +} + +void ImportClause(): {} +{ + + ( + LOOKAHEAD(2) (SimpleName() Name()) #RenamingImportClause + | LOOKAHEAD(Name() (( ) | )) (Name() (( ) | )) #UnqualifiedImportClause + | LOOKAHEAD(Name() ) ((Name() ImportList()) ) #MultipleDefinitionImportClause + | Name() #SingleDefinitionImportClause + ) + Comment() +} + +void ImportList(): {} +{ + SimpleName() + ( + + SimpleName() + )* +} + +void ExtendsClause(): {} +{ + + Name() + (ClassModification())? + (Annotation())? +} + +void ConstrainingClause(): {} +{ + + Name() + (ClassModification())? +} + +void ComponentClause(): {} +{ + TypePrefix() + TypeSpecifier() + (ArraySubscripts())? + ComponentList() +} + +void TypePrefix(): {} +{ + ( #FlowClause | #StreamClause)? + ( #DiscreteClause | #ParameterClause | #ConstantClause)? + ( #InputClause | #OutputClause)? +} + +void TypeSpecifier(): {} +{ + Name() +} + +void ComponentList(): {} +{ + ComponentDeclaration() + ( + + ComponentDeclaration() + )* +} + +void ComponentDeclaration(): {} +{ + Declaration() + (ConditionAttribute())? + Comment() +} + +void ConditionAttribute(): {} +{ + + Expression() +} + +void Declaration(): {} +{ + SimpleName() + (ArraySubscripts())? + (Modification())? +} + +void Modification() #void: {} +{ + ( + ClassModification() + ( + + Expression() + )? + ) #LongModification + | ( + + Expression() + ) #ShortModification + | ( + + Expression() + ) #AssignmentModification +} + +void ClassModification(): {} +{ + + (ArgumentList())? + +} + +void ArgumentList(): {} +{ + Argument() + ( + + Argument() + )* +} + +void Argument(): {} +{ + ElementModificationOrReplaceable() + | ElementRedeclaration() +} + +void ElementModificationOrReplaceable(): {} +{ + ( #EachClause)? + ( #FinalClause)? + ( + ElementModification() + | ElementReplaceable() + ) +} + +void ElementModification(): {} +{ + Name() + (Modification())? + StringComment() +} + +void ElementRedeclaration(): {} +{ + + ( #EachClause)? + ( #FinalClause)? + ( + (ShortClassDefinition() | ComponentClause1()) + | ElementReplaceable() + ) +} + +void ElementReplaceable(): {} +{ + + (ShortClassDefinition() | ComponentClause1()) + (ConstrainingClause())? +} + +void ComponentClause1(): {} +{ + TypePrefix() + TypeSpecifier() + ComponentDeclaration1() +} + +void ComponentDeclaration1(): {} +{ + Declaration() + Comment() +} + +void ShortClassDefinition(): {} +{ + ClassPrefixes() + ShortClassSpecifier() +} + +void EquationSection(): {} +{ + ( #InitialClause)? + + ( + LOOKAHEAD(2) Equation() + + )* +} + +void AlgorithmSection(): {} +{ + ( #InitialClause)? + + ( + Statement() + + )* +} + +void Equation(): {} +{ + ( + LOOKAHEAD (SimpleExpression() Expression() /* TODO */) (SimpleExpression() Expression()) #RegularEquation + | IfEquation() + | ForEquation() + | ConnectClause() + | WhenEquation() + | (Name() FunctionCallArgs()) #FunctionCallEquation + ) + Comment() +} + +void Statement(): {} +{ + ( + LOOKAHEAD(ComponentReference() ) (ComponentReference() Expression()) #AssignmentStatement + | (ComponentReference() FunctionCallArgs()) #FunctionCallStatement + | ( OutputExpressionList() ComponentReference() FunctionCallArgs()) #AssignmentFromMultiResultFunctionCall + | #BreakStatement + | #ReturnStatement + | IfStatement() + | ForStatement() + | WhileStatement() + | WhenStatement() + ) + Comment() +} + +void IfEquation(): {} +{ + ( Expression() #IfClause) + ( EquationList() #ThenClause) + (( Expression() EquationList()) #ElseIfClause)* + ( EquationList() #ElseClause)? + +} + +void IfStatement(): {} +{ + ( Expression() #IfClause) + ( StatementList() #ThenClause) + (( Expression() StatementList()) #ElseIfClause)* + ( StatementList() #ElseClause)? + +} + +void ForEquation(): {} +{ + + ForIndices() + + EquationList() + +} + +void EquationList(): {} +{ + (LOOKAHEAD(Equation() /* TODO */) Equation() )* +} + +void StatementList(): {} +{ + (Statement() )* +} + +void ForStatement(): {} +{ + + ForIndices() + + StatementList() + +} + +void ForIndices(): {} +{ + ForIndex() + ( ForIndex())* +} + +void ForIndex(): {} +{ + SimpleName() + ( + + Expression() + )? +} + +void WhileStatement(): {} +{ + + Expression() + + StatementList() + +} + +void WhenEquation(): {} +{ + ( Expression() #WhenClause) + ( EquationList() #ThenClause) + (( Expression() EquationList()) #ElseWhenClause)* + +} + +void WhenStatement(): {} +{ + ( Expression() #WhenClause) + ( StatementList() #ThenClause) + (( Expression() StatementList()) #ElseWhenClause)* + +} + +void ConnectClause(): {} +{ + + + ComponentReference() + + ComponentReference() + +} + +void Expression() #void: {} +{ + SimpleExpression() + | IfExpression() +} + +void IfExpression(): {} +{ + (( Expression()) #IfClause) + (( Expression()) #ThenClause) + (( Expression() Expression()) #ElseIfClause)* + (( Expression()) #ElseClause) +} + +void SimpleExpression() #SimpleExpression(>1): {} +{ + LogicalExpression() + ( + + LogicalExpression() + ( + + LogicalExpression() + )? + )? +} + +void LogicalExpression() #LogicalExpression(>1): {} +{ + LogicalTerm() + ( + + LogicalTerm() + )* +} + +void LogicalTerm() #LogicalTerm(>1): {} +{ + LogicalFactor() + ( + + LogicalFactor() + )* +} + +void LogicalFactor() #void: {} +{ + ( Relation()) #Negated + | Relation() +} + +void Relation() #Relation(>1): {} +{ + ArithmeticExpression() + ( + RelOp() + ArithmeticExpression() + )? +} + +void RelOp(): {} +{ + { jjtThis.setImage("<"); } + | { jjtThis.setImage("<="); } + | { jjtThis.setImage(">"); } + | { jjtThis.setImage(">="); } + | { jjtThis.setImage("=="); } + | { jjtThis.setImage("<>"); } +} + +void ArithmeticExpression() #ArithmeticExpression(>1): {} +{ + (AddOp())? + Term() + ( + AddOp() + Term() + )* +} + +void AddOp(): {} +{ + { jjtThis.setImage("+"); } + | { jjtThis.setImage("-"); } + | { jjtThis.setImage(".+"); } + | { jjtThis.setImage(".-"); } +} + +void Term() #Term(>1): {} +{ + Factor() + ( + MulOp() + Factor() + )* +} + +void MulOp(): {} +{ + { jjtThis.setImage("*"); } + | { jjtThis.setImage("/"); } + | { jjtThis.setImage(".*"); } + | { jjtThis.setImage("./"); } +} + +void Factor() #Factor(>1): {} +{ + Primary() + ( + ( + { jjtThis.setImage("^"); } + | { jjtThis.setImage(".^"); } + ) + Primary() + )? +} + +void Primary() #void: {} +{ + NumberLiteral() + | StringLiteral() + | #FalseLiteral + | #TrueLiteral + | LOOKAHEAD((((Name() | | ) FunctionCallArgs())) /* TODO */) (((Name() | #DerClause | #InitialClause) FunctionCallArgs())) #FunctionInvocation + | ComponentReference() + | ( OutputExpressionList() ) + | ( ExpressionList() ( ExpressionList())* ) #ListOfExpressionLists + | ( FunctionArguments() ) + | +} + +void NumberLiteral(): +{ + Token t; +} +{ + t = + { jjtThis.setImage(t.image); } +} + +void StringLiteral(): +{ + Token t; +} +{ + t = + { jjtThis.setImage(t.image); } +} + +void Name(): {} +{ + ( { jjtThis.markAbsolute(); } )? + SimpleName() + (LOOKAHEAD(2) ( + + SimpleName() + ))* +} + +void SimpleName(): +{ + Token t; +} +{ + t = + { jjtThis.setImage(t.image); } +} + +void SubscriptedName(): {} +{ + SimpleName() + (ArraySubscripts())? +} + +void ComponentReference(): {} +{ + ( { jjtThis.markAbsolute(); } )? + SubscriptedName() + ( + + SubscriptedName() + )* +} + +void FunctionCallArgs(): {} +{ + + (FunctionArguments())? + +} + +void FunctionArguments(): {} +{ + LOOKAHEAD(2) NamedArguments() + | (FunctionArgument() ( FunctionArguments() | ( ForIndices()))?) +} + +void NamedArguments(): {} +{ + NamedArgument() + ( + + NamedArguments() + )? +} + +void NamedArgument(): {} +{ + SimpleName() + + FunctionArgument() +} + +void FunctionArgument(): {} +{ + ( + + Name() + + (NamedArguments())? + + ) | ( + Expression() + ) +} + +void OutputExpressionList(): {} +{ + (Expression())? + ( + + (Expression())? + )* +} + +void ExpressionList(): {} +{ + Expression() + ( + + Expression() + )* +} + +void ArraySubscripts(): {} +{ + + Subscript() + ( + + Subscript() + )* + +} + +void Subscript(): {} +{ + ( #ColonSubsript) + | Expression() +} + +void Comment(): {} +{ + StringComment() + (Annotation())? +} + +void StringComment() #StringComment(!skip): +{ + boolean skip = true; + StringBuilder sb = new StringBuilder(); + Token t; +} +{ + (( + t = + { sb.append(t); } + ( t = { sb.append(t); } )* + ) { skip = false; }) ? + { jjtThis.setImage(sb.toString()); } +} + +void Annotation(): {} +{ + + ClassModification() +} diff --git a/pmd-modelica/pom.xml b/pmd-modelica/pom.xml new file mode 100644 index 0000000000..3993c84a4c --- /dev/null +++ b/pmd-modelica/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + pmd-modelica + PMD Modelica + + + net.sourceforge.pmd + pmd + 6.21.0-SNAPSHOT + + + + + + maven-resources-plugin + + false + + ${*} + + + + + + org.apache.maven.plugins + maven-antrun-plugin + true + + + generate-sources + generate-sources + + + + + + + + + + run + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-javacc-generated-sources + + add-source + + + + ${project.build.directory}/generated-sources/javacc + + + + + + + + + + org.antlr + antlr4-runtime + + + net.sourceforge.pmd + pmd-core + + + + net.sourceforge.pmd + pmd-test + test + + + \ No newline at end of file diff --git a/pmd-modelica/src/main/ant/alljavacc.xml b/pmd-modelica/src/main/ant/alljavacc.xml new file mode 100644 index 0000000000..0686a4811d --- /dev/null +++ b/pmd-modelica/src/main/ant/alljavacc.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public class + + + + + + + + + + public class Token implements java.io.Serializable + + + + + + public Token specialToken; + + + + + + + SimpleNode + AbstractModelicaNode + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java new file mode 100644 index 0000000000..53852955e5 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java @@ -0,0 +1,13 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule; + +public class ModelicaLanguage extends AbstractLanguage { + public ModelicaLanguage() { + super(ModelicaLanguageModule.NAME, ModelicaLanguageModule.TERSE_NAME, new ModelicaTokenizer(), ".mo"); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java new file mode 100644 index 0000000000..e688b3dcfa --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java @@ -0,0 +1,67 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import java.io.StringReader; + +import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.GenericToken; +import net.sourceforge.pmd.lang.modelica.ModelicaTokenManager; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaParser; +import net.sourceforge.pmd.lang.modelica.ast.Token; + + +public class ModelicaTokenizer extends JavaCCTokenizer { + @Override + protected TokenManager getLexerForSource(SourceCode sourceCode) { + final StringBuilder stringBuilder = sourceCode.getCodeBuffer(); + return new ModelicaTokenManager(new StringReader(stringBuilder.toString())); + } + + @Override + protected JavaCCTokenFilter getTokenFilter(TokenManager tokenManager) { + return new ModelicaTokenFilter(tokenManager); + } + + public static class ModelicaTokenFilter extends JavaCCTokenFilter { + private boolean discardingWithinAndImport = false; + private boolean discardingAnnotation = false; + + ModelicaTokenFilter(TokenManager tokenManager) { + super(tokenManager); + } + + private void skipWithinAndImport(Token currentToken) { + final int type = currentToken.kind; + if (type == ModelicaParser.IMPORT || type == ModelicaParser.WITHIN) { + discardingWithinAndImport = true; + } else if (discardingWithinAndImport && type == ModelicaParser.SC) { + discardingWithinAndImport = false; + } + } + + private void skipAnnotation(Token currentToken) { + final int type = currentToken.kind; + if (type == ModelicaParser.ANNOTATION) { + discardingAnnotation = true; + } else if (discardingAnnotation && type == ModelicaParser.SC) { + discardingAnnotation = false; + } + } + + @Override + protected void analyzeToken(GenericToken currentToken) { + skipWithinAndImport((Token) currentToken); + skipAnnotation((Token) currentToken); + } + + @Override + protected boolean isLanguageSpecificDiscarding() { + return discardingWithinAndImport || discardingAnnotation; + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java new file mode 100644 index 0000000000..cfb5df5bb0 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java @@ -0,0 +1,49 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica; + +import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.VisitorStarter; +import net.sourceforge.pmd.lang.XPathHandler; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaSymbolFacade; +import net.sourceforge.pmd.lang.modelica.rule.ModelicaRuleViolationFactory; +import net.sourceforge.pmd.lang.rule.RuleViolationFactory; + +public class ModelicaHandler extends AbstractLanguageVersionHandler { + @Override + public XPathHandler getXPathHandler() { + return new DefaultASTXPathHandler(); + } + + @Override + public RuleViolationFactory getRuleViolationFactory() { + return ModelicaRuleViolationFactory.INSTANCE; + } + + @Override + public Parser getParser(ParserOptions parserOptions) { + return new ModelicaParser(parserOptions); + } + + @Override + public VisitorStarter getSymbolFacade() { + return new VisitorStarter() { + @Override + public void start(Node rootNode) { + new ModelicaSymbolFacade().initializeWith((ASTStoredDefinition) rootNode); + } + }; + } + + @Override + public VisitorStarter getSymbolFacade(ClassLoader classLoader) { + return getSymbolFacade(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java new file mode 100644 index 0000000000..2327f90c55 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica; + +import net.sourceforge.pmd.lang.BaseLanguageModule; +import net.sourceforge.pmd.lang.modelica.rule.ModelicaRuleChainVisitor; + +public class ModelicaLanguageModule extends BaseLanguageModule { + public static final String NAME = "Modelica"; + public static final String TERSE_NAME = "modelica"; + + public ModelicaLanguageModule() { + super(NAME, null, TERSE_NAME, ModelicaRuleChainVisitor.class, "mo"); + addVersion("", new ModelicaHandler(), true); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaParser.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaParser.java new file mode 100644 index 0000000000..a72144af07 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaParser.java @@ -0,0 +1,45 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica; + +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; + +import net.sourceforge.pmd.lang.AbstractParser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.AbstractTokenManager; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.ParseException; +import net.sourceforge.pmd.lang.ast.SimpleCharStream; + + +public class ModelicaParser extends AbstractParser { + public ModelicaParser(final ParserOptions parserOptions) { + super(parserOptions); + } + + @Override + protected TokenManager createTokenManager(Reader source) { + return new ModelicaTokenManager(source); + } + + @Override + public boolean canParse() { + return true; + } + + @Override + public Node parse(String fileName, Reader source) throws ParseException { + AbstractTokenManager.setFileName(fileName); + return new net.sourceforge.pmd.lang.modelica.ast.ModelicaParser(new SimpleCharStream(source)).StoredDefinition(); + } + + @Override + public Map getSuppressMap() { + return new HashMap(); // TODO + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaTokenManager.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaTokenManager.java new file mode 100644 index 0000000000..5fc1a862da --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaTokenManager.java @@ -0,0 +1,31 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica; + +import java.io.Reader; + +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.AbstractTokenManager; +import net.sourceforge.pmd.lang.ast.SimpleCharStream; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserTokenManager; + + +public class ModelicaTokenManager implements TokenManager { + private final ModelicaParserTokenManager modelicaParserTokenManager; + + public ModelicaTokenManager(final Reader source) { + modelicaParserTokenManager = new ModelicaParserTokenManager(new SimpleCharStream(source)); + } + + @Override + public Object getNextToken() { + return modelicaParserTokenManager.getNextToken(); + } + + @Override + public void setFileName(String fileName) { + AbstractTokenManager.setFileName(fileName); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTClassDefinition.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTClassDefinition.java new file mode 100644 index 0000000000..9a7fd3eae8 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTClassDefinition.java @@ -0,0 +1,78 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassSpecialization; + +public class ASTClassDefinition extends AbstractModelicaNode { + private ASTClassPrefixes prefixes; + private ModelicaClassSpecialization specialization; + private ModelicaClassSpecifierNode specifier; + + public ASTClassDefinition(int id) { + super(id); + } + + public ASTClassDefinition(ModelicaParser p, int id) { + super(p, id); + } + + public boolean isPartial() { + return prefixes.getFirstChildOfType(ASTPartialClause.class) != null; + } + + public boolean isEncapsulated() { + return getFirstChildOfType(ASTEncapsulatedClause.class) != null; + } + + public ModelicaClassSpecialization getSpecialization() { + return specialization; + } + + public ModelicaClassSpecifierNode getClassSpecifier() { + return specifier; + } + + private void checkSpecialization(Class clauseClass, ModelicaClassSpecialization restriction) { + if (prefixes.getFirstChildOfType(clauseClass) != null) { + assert specialization == null; + specialization = restriction; + } + } + + private void detectSpecialization() { + checkSpecialization(ASTClassClause.class, ModelicaClassSpecialization.CLASS); + checkSpecialization(ASTModelClause.class, ModelicaClassSpecialization.MODEL); + checkSpecialization(ASTRecordClause.class, ModelicaClassSpecialization.RECORD); + checkSpecialization(ASTOperatorRecordClause.class, ModelicaClassSpecialization.OPERATOR_RECORD); + checkSpecialization(ASTBlockClause.class, ModelicaClassSpecialization.BLOCK); + checkSpecialization(ASTConnectorClause.class, ModelicaClassSpecialization.CONNECTOR); + checkSpecialization(ASTExpandableConnectorClause.class, ModelicaClassSpecialization.EXPANDABLE_CONNECTOR); + checkSpecialization(ASTTypeClause.class, ModelicaClassSpecialization.TYPE); + checkSpecialization(ASTPackageClause.class, ModelicaClassSpecialization.PACKAGE); + checkSpecialization(ASTOperatorClause.class, ModelicaClassSpecialization.OPERATOR); + ASTFunctionClause functionOrNull = prefixes.getFirstChildOfType(ASTFunctionClause.class); + if (functionOrNull != null) { + boolean isPure = functionOrNull.getFirstChildOfType(ASTPureClause.class) != null; + boolean isOperator = functionOrNull.getFirstChildOfType(ASTOperatorClause.class) != null; + assert specialization == null; + specialization = ModelicaClassSpecialization.getFunctionSpecialization(isPure, isOperator); + } + assert specialization != null; + } + + @Override + public void jjtClose() { + super.jjtClose(); + prefixes = getFirstChildOfType(ASTClassPrefixes.class); + specifier = getFirstChildOfType(ASTClassSpecifier.class).getFirstChildOfType(ModelicaClassSpecifierNode.class); + detectSpecialization(); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTComponentReference.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTComponentReference.java new file mode 100644 index 0000000000..1a91e55b54 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTComponentReference.java @@ -0,0 +1,77 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.CompositeName; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; +import net.sourceforge.pmd.lang.modelica.resolver.ResolvableEntity; + +public final class ASTComponentReference extends AbstractModelicaNode implements ResolvableModelicaNode { + private String[] nameComponentsWithoutSubscripts; + private boolean absolute; + private ResolutionResult resolutionCandidates; + + ASTComponentReference(int id) { + super(id); + } + + ASTComponentReference(ModelicaParser p, int id) { + super(p, id); + } + + void markAbsolute() { + absolute = true; + } + + /** + * Returns whether this reference is absolute (starts with a dot), such as + * y = .Modelica.Math.cos(x). + */ + boolean isAbsolute() { + return absolute; + } + + /** + * Returns a {@link CompositeName} object representing the lexical reference with subscripts being ignored, if any. + */ + public CompositeName getCompositeNameWithoutSubscripts() { + return CompositeName.create(absolute, nameComponentsWithoutSubscripts); + } + + /** + * Returns resolution candidates for the referred component (and not dereferencing its type, etc.). + * + * We do not decide on entity type on behalf of the rule code, since this may introduce false negatives. + */ + @Override + public ResolutionResult getResolutionCandidates() { + if (resolutionCandidates == null) { + resolutionCandidates = getMostSpecificScope().safeResolveLexically(ResolvableEntity.class, ResolutionState.forComponentReference(), getCompositeNameWithoutSubscripts()); + } + return resolutionCandidates; + } + + // For Rule Designer + public String getResolvedTo() { + return Helper.getResolvedTo(getResolutionCandidates()); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + + nameComponentsWithoutSubscripts = new String[jjtGetNumChildren()]; + for (int i = 0; i < nameComponentsWithoutSubscripts.length; ++i) { + String name = jjtGetChild(i).getFirstChildOfType(ASTSimpleName.class).getImage(); + nameComponentsWithoutSubscripts[i] = name; + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTDerClassSpecifier.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTDerClassSpecifier.java new file mode 100644 index 0000000000..e3b460909a --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTDerClassSpecifier.java @@ -0,0 +1,20 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public final class ASTDerClassSpecifier extends AbstractModelicaClassSpecifierNode { + ASTDerClassSpecifier(int id) { + super(id); + } + + ASTDerClassSpecifier(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTElementList.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTElementList.java new file mode 100644 index 0000000000..52831570c4 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTElementList.java @@ -0,0 +1,30 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public class ASTElementList extends AbstractModelicaNode { + private Visibility visibility; + + public ASTElementList(int id) { + super(id); + } + + public ASTElementList(ModelicaParser p, int id) { + super(p, id); + } + + void setVisibility(Visibility visibility) { + this.visibility = visibility; + } + + public Visibility getVisibility() { + return visibility; + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTEnumerationShortClassSpecifier.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTEnumerationShortClassSpecifier.java new file mode 100644 index 0000000000..85e9477c15 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTEnumerationShortClassSpecifier.java @@ -0,0 +1,20 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public final class ASTEnumerationShortClassSpecifier extends AbstractModelicaClassSpecifierNode { + ASTEnumerationShortClassSpecifier(int id) { + super(id); + } + + ASTEnumerationShortClassSpecifier(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTExtendingLongClassSpecifier.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTExtendingLongClassSpecifier.java new file mode 100644 index 0000000000..3b6f0d1ffd --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTExtendingLongClassSpecifier.java @@ -0,0 +1,29 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassType; + +public final class ASTExtendingLongClassSpecifier extends AbstractModelicaClassSpecifierNode { + ASTExtendingLongClassSpecifier(int id) { + super(id); + } + + ASTExtendingLongClassSpecifier(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void populateExtendsAndImports(ModelicaClassType classTypeDeclaration) { + super.populateExtendsAndImports(classTypeDeclaration); + pushExtendsAndImports(classTypeDeclaration, getFirstChildOfType(ASTComposition.class)); + // TODO + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTMultipleDefinitionImportClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTMultipleDefinitionImportClause.java new file mode 100644 index 0000000000..6299466cf1 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTMultipleDefinitionImportClause.java @@ -0,0 +1,63 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import java.util.HashSet; +import java.util.Set; + +import net.sourceforge.pmd.lang.modelica.resolver.CompositeName; +import net.sourceforge.pmd.lang.modelica.resolver.InternalModelicaResolverApi; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaDeclaration; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionContext; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; +import net.sourceforge.pmd.lang.modelica.resolver.Watchdog; + +public final class ASTMultipleDefinitionImportClause extends AbstractModelicaImportClause { + private ASTName importFrom; + private Set importedNames = new HashSet<>(); + + ASTMultipleDefinitionImportClause(int id) { + super(id); + } + + ASTMultipleDefinitionImportClause(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + importFrom = getFirstChildOfType(ASTName.class); + ASTImportList importList = getFirstChildOfType(ASTImportList.class); + for (int i = 0; i < importList.jjtGetNumChildren(); ++i) { + ASTSimpleName namePart = (ASTSimpleName) importList.jjtGetChild(i); + importedNames.add(namePart.getImage()); + } + } + + @Override + protected ResolutionResult getCacheableImportSources(ResolutionState state, ModelicaScope scope) { + return scope.safeResolveLexically(ModelicaDeclaration.class, state, importFrom.getCompositeName()); + } + + @Override + protected void fetchImportedClassesFromSource(ResolutionContext result, ModelicaDeclaration source, String simpleName) throws Watchdog.CountdownException { + if (importedNames.contains(simpleName)) { + InternalModelicaResolverApi.resolveFurtherNameComponents(source, result, CompositeName.create(simpleName)); + } + } + + @Override + public boolean isQualified() { + return true; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTName.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTName.java new file mode 100644 index 0000000000..e408225279 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTName.java @@ -0,0 +1,85 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.CompositeName; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; +import net.sourceforge.pmd.lang.modelica.resolver.ResolvableEntity; + +public final class ASTName extends AbstractModelicaNode implements ResolvableModelicaNode { + private String[] nameComponents; + private ResolutionResult resolutionCandidates; + private boolean absolute = false; + + ASTName(int id) { + super(id); + } + + ASTName(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < jjtGetNumChildren(); ++i) { + if (i != 0 || absolute) { + sb.append('.'); + } + sb.append(((ASTSimpleName) jjtGetChild(i)).getImage()); + } + setImage(sb.toString()); + + nameComponents = new String[jjtGetNumChildren()]; + for (int i = 0; i < jjtGetNumChildren(); ++i) { + nameComponents[i] = jjtGetChild(i).getImage(); + } + } + + void markAbsolute() { + absolute = true; + } + + /** + * Returns whether this reference is absolute (starts with a dot), such as + * .Modelica.Blocks.Continuous.Filter. + */ + public boolean isAbsolute() { + return absolute; + } + + /** + * Returns a {@link CompositeName} object representing a lexical reference contained in this node. + */ + public CompositeName getCompositeName() { + return CompositeName.create(absolute, nameComponents); + } + + /** + * Returns resolution candidates for the referred entity. + * + * We do not decide on entity type on behalf of the rule code, since this may introduce false negatives. + */ + @Override + public ResolutionResult getResolutionCandidates() { + if (resolutionCandidates == null) { + resolutionCandidates = getMostSpecificScope().safeResolveLexically(ResolvableEntity.class, ResolutionState.forType(), getCompositeName()); + } + return resolutionCandidates; + } + + // For Rule Designer + public String getResolvedTo() { + return Helper.getResolvedTo(getResolutionCandidates()); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTRenamingImportClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTRenamingImportClause.java new file mode 100644 index 0000000000..fcb52bfe83 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTRenamingImportClause.java @@ -0,0 +1,54 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaDeclaration; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionContext; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; + +public final class ASTRenamingImportClause extends AbstractModelicaImportClause { + private ASTName importWhat; + private String renamedTo; + + ASTRenamingImportClause(int id) { + super(id); + } + + ASTRenamingImportClause(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + + importWhat = getFirstChildOfType(ASTName.class); + renamedTo = getFirstChildOfType(ASTSimpleName.class).getImage(); + } + + @Override + protected ResolutionResult getCacheableImportSources(ResolutionState state, ModelicaScope scope) { + return scope.safeResolveLexically(ModelicaDeclaration.class, state, importWhat.getCompositeName()); + } + + @Override + protected void fetchImportedClassesFromSource(ResolutionContext result, ModelicaDeclaration source, String simpleName) { + if (renamedTo.equals(simpleName)) { + result.addCandidate(source); + } + } + + @Override + public boolean isQualified() { + return true; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleLongClassSpecifier.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleLongClassSpecifier.java new file mode 100644 index 0000000000..f33f0d75f1 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleLongClassSpecifier.java @@ -0,0 +1,28 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassType; + +public final class ASTSimpleLongClassSpecifier extends AbstractModelicaClassSpecifierNode { + ASTSimpleLongClassSpecifier(int id) { + super(id); + } + + ASTSimpleLongClassSpecifier(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + void populateExtendsAndImports(ModelicaClassType classTypeDeclaration) { + super.populateExtendsAndImports(classTypeDeclaration); + pushExtendsAndImports(classTypeDeclaration, getFirstChildOfType(ASTComposition.class)); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleShortClassSpecifier.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleShortClassSpecifier.java new file mode 100644 index 0000000000..74d472a05b --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSimpleShortClassSpecifier.java @@ -0,0 +1,33 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.InternalModelicaResolverApi; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassType; + +public final class ASTSimpleShortClassSpecifier extends AbstractModelicaClassSpecifierNode { + ASTSimpleShortClassSpecifier(int id) { + super(id); + } + + ASTSimpleShortClassSpecifier(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void populateExtendsAndImports(ModelicaClassType classTypeDeclaration) { + super.populateExtendsAndImports(classTypeDeclaration); + InternalModelicaResolverApi.addExtendToClass( + classTypeDeclaration, + Visibility.UNSPEC, + getFirstChildOfType(ASTName.class).getCompositeName() + ); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSingleDefinitionImportClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSingleDefinitionImportClause.java new file mode 100644 index 0000000000..3ca3ab7ebb --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTSingleDefinitionImportClause.java @@ -0,0 +1,54 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaDeclaration; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionContext; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; + +public class ASTSingleDefinitionImportClause extends AbstractModelicaImportClause { + private ASTName importWhat; + private String importedName; + + ASTSingleDefinitionImportClause(int id) { + super(id); + } + + ASTSingleDefinitionImportClause(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + + importWhat = getFirstChildOfType(ASTName.class); + importedName = importWhat.jjtGetChild(importWhat.jjtGetNumChildren() - 1).getImage(); + } + + @Override + protected ResolutionResult getCacheableImportSources(ResolutionState state, ModelicaScope scope) { + return scope.safeResolveLexically(ModelicaDeclaration.class, state, importWhat.getCompositeName()); + } + + @Override + protected void fetchImportedClassesFromSource(ResolutionContext result, ModelicaDeclaration source, String simpleName) { + if (importedName.equals(simpleName)) { + result.addCandidate(source); + } + } + + @Override + public boolean isQualified() { + return true; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTStoredDefinition.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTStoredDefinition.java new file mode 100644 index 0000000000..886c7ec9c1 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTStoredDefinition.java @@ -0,0 +1,55 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.CompositeName; + +/** + * A representation of a Modelica source code file. + */ +public class ASTStoredDefinition extends AbstractModelicaNode { + private boolean hasBOM = false; + + ASTStoredDefinition(int id) { + super(id); + } + + ASTStoredDefinition(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + void markHasBOM() { + hasBOM = true; + } + + /** + * Returns whether this source file contains Byte Order Mark. + */ + public boolean getHasBOM() { + return hasBOM; + } + + @Override + public void jjtClose() { + super.jjtClose(); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < jjtGetNumChildren(); ++i) { + AbstractModelicaNode child = (AbstractModelicaNode) jjtGetChild(i); + if (child instanceof ASTWithinClause) { + if (sb.length() > 0) { + sb.append(CompositeName.NAME_COMPONENT_SEPARATOR); + } + sb.append(child.getImage()); + } + } + setImage(sb.toString()); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTUnqualifiedImportClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTUnqualifiedImportClause.java new file mode 100644 index 0000000000..15b3c53b5a --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTUnqualifiedImportClause.java @@ -0,0 +1,54 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.CompositeName; +import net.sourceforge.pmd.lang.modelica.resolver.InternalModelicaResolverApi; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaDeclaration; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionContext; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; +import net.sourceforge.pmd.lang.modelica.resolver.Watchdog; + +public final class ASTUnqualifiedImportClause extends AbstractModelicaImportClause { + private ASTName importFromWhere; + + ASTUnqualifiedImportClause(int id) { + super(id); + } + + ASTUnqualifiedImportClause(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + + importFromWhere = getFirstChildOfType(ASTName.class); + } + + @Override + protected ResolutionResult getCacheableImportSources(ResolutionState state, ModelicaScope scope) { + return scope.safeResolveLexically(ModelicaDeclaration.class, state, importFromWhere.getCompositeName()); + } + + @Override + protected void fetchImportedClassesFromSource(ResolutionContext result, ModelicaDeclaration source, String simpleName) throws Watchdog.CountdownException { + result.watchdogTick(); + InternalModelicaResolverApi.resolveFurtherNameComponents(source, result, CompositeName.create(simpleName)); + } + + @Override + public boolean isQualified() { + return false; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTWithinClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTWithinClause.java new file mode 100644 index 0000000000..827398a1a9 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ASTWithinClause.java @@ -0,0 +1,32 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public final class ASTWithinClause extends AbstractModelicaNode { + ASTWithinClause(int id) { + super(id); + } + + ASTWithinClause(ModelicaParser p, int id) { + super(p, id); + } + + @Override + public Object jjtAccept(ModelicaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + @Override + public void jjtClose() { + super.jjtClose(); + + ASTName name = getFirstChildOfType(ASTName.class); + if (name != null) { + setImage(name.getImage()); + } else { + setImage(""); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaClassSpecifierNode.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaClassSpecifierNode.java new file mode 100644 index 0000000000..b39cc35768 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaClassSpecifierNode.java @@ -0,0 +1,70 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.InternalModelicaResolverApi; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassType; + +/** + * Common parent for class-specifier nodes, see {@link ModelicaClassSpecifierNode} for public API. + */ +abstract class AbstractModelicaClassSpecifierNode extends AbstractModelicaNode implements ModelicaClassSpecifierNode { + AbstractModelicaClassSpecifierNode(int id) { + super(id); + } + + AbstractModelicaClassSpecifierNode(ModelicaParser parser, int id) { + super(parser, id); + } + + @Override + public void jjtClose() { + super.jjtClose(); + setImage(getFirstChildOfType(ASTSimpleName.class).getImage()); + } + + /** + * Fills in the class definition with extends and import clauses contained in this AST node. + * + * @param classTypeDeclaration a class declaration object corresponding to this AST node + */ + void populateExtendsAndImports(ModelicaClassType classTypeDeclaration) { + // by default, do nothing + } + + private void pushExtendsAndImportsFromList(ModelicaClassType classTypeDeclaration, ASTElementList listNode) { + for (int i = 0; i < listNode.jjtGetNumChildren(); ++i) { + AbstractModelicaNode child = (AbstractModelicaNode) listNode.jjtGetChild(i); + if (child instanceof ASTExtendsClause) { + InternalModelicaResolverApi.addExtendToClass( + classTypeDeclaration, + listNode.getVisibility(), + child.getFirstChildOfType(ASTName.class).getCompositeName() + ); + } + if (child instanceof ASTImportClause) { + InternalModelicaResolverApi.addImportToClass( + classTypeDeclaration, + listNode.getVisibility(), + child.getFirstChildOfType(ModelicaImportClause.class) + ); + } + } + } + + void pushExtendsAndImports(ModelicaClassType classTypeDeclaration, ASTComposition composition) { + for (int i = 0; i < composition.jjtGetNumChildren(); ++i) { + ModelicaNode maybeElementList = composition.jjtGetChild(i); + if (maybeElementList instanceof ASTElementList) { + pushExtendsAndImportsFromList(classTypeDeclaration, (ASTElementList) maybeElementList); + } + } + } + + @Override + public String getSimpleClassName() { + return getImage(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaImportClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaImportClause.java new file mode 100644 index 0000000000..06a626c790 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaImportClause.java @@ -0,0 +1,68 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaDeclaration; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionContext; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionState; +import net.sourceforge.pmd.lang.modelica.resolver.Watchdog; + +/** + * Common internal machinery for various import clauses to describe themselves to resolver. + */ +abstract class AbstractModelicaImportClause extends AbstractModelicaNode implements ModelicaImportClause { + private ResolutionResult importSourcesCache; + + AbstractModelicaImportClause(int id) { + super(id); + } + + AbstractModelicaImportClause(ModelicaParser parser, int id) { + super(parser, id); + } + + /** + * Some import clauses are considered "qualified", some "unqualified", the former being processed first + * while looking up some name. See "5.3.1 Simple Name Lookup" from MLS 3.4. + * + * @return Whether this kind of import is considered "qualified" + */ + abstract boolean isQualified(); + + /** + * A template method to be used by {@link resolveSimpleName}. Usually used to fetch the lexically referenced + * class in the corresponding import statement. + */ + protected abstract ResolutionResult getCacheableImportSources(ResolutionState state, ModelicaScope scope); + + /** + * A template method to be used by {@link resolveSimpleName}. Usually used to try to fetch declarations for + * simpleName from particular source returned by {@link getCacheableImportSources}. + */ + protected abstract void fetchImportedClassesFromSource(ResolutionContext result, ModelicaDeclaration source, String simpleName) throws Watchdog.CountdownException; + + /** + * Tries to resolve the specified name via this import clause. + * + * @param result Resolution context + * @param simpleName Name to resolve + * @throws Watchdog.CountdownException if too many resolution steps were performed + */ + final void resolveSimpleName(ResolutionContext result, String simpleName) throws Watchdog.CountdownException { + // No need to re-resolve if already resolved successfully + if (importSourcesCache == null || importSourcesCache.wasTimedOut()) { + importSourcesCache = getCacheableImportSources(result.getState(), getMostSpecificScope().getParent()); + } + for (ModelicaDeclaration source : importSourcesCache.getBestCandidates()) { + fetchImportedClassesFromSource(result, source, simpleName); + } + result.markHidingPoint(); + for (ModelicaDeclaration source : importSourcesCache.getHiddenCandidates()) { + fetchImportedClassesFromSource(result, source, simpleName); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaNode.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaNode.java new file mode 100644 index 0000000000..08e050d612 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaNode.java @@ -0,0 +1,93 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.ast.AbstractNode; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; + +/** + * Abstract base class for all nodes parsed by {@link ModelicaParser}. + * + * Please note that some of these nodes are autogenerated and placed to + * pmd-modelica/target/generated-sources/javacc by the build script. Those + * located here are deleted from autogenerated sources after build. + * + * @see ModelicaNode for public API. + */ +abstract class AbstractModelicaNode extends AbstractNode implements Node, ModelicaNode { + private ModelicaParser parser; + private ModelicaScope ownScope; + + AbstractModelicaNode(int id) { + super(id); + } + + AbstractModelicaNode(ModelicaParser parser, int id) { + super(id); + this.parser = parser; + } + + @Override + public abstract Object jjtAccept(ModelicaParserVisitor visitor, Object data); + + @Override + public String getXPathNodeName() { + return getClass().getSimpleName().substring(3); + } + + @Override + public ModelicaNode jjtGetParent() { + return (ModelicaNode) super.jjtGetParent(); + } + + @Override + public ModelicaNode jjtGetChild(int index) { + return (ModelicaNode) super.jjtGetChild(index); + } + + @Override + public void jjtOpen() { + if (beginLine == -1 && parser.token.next != null) { + beginLine = parser.token.next.beginLine; + beginColumn = parser.token.next.beginColumn; + } + } + + @Override + public void jjtClose() { + if (beginLine == -1 && (children == null || children.length == 0)) { + beginColumn = parser.token.beginColumn; + } + if (beginLine == -1) { + beginLine = parser.token.beginLine; + } + endLine = parser.token.endLine; + endColumn = parser.token.endColumn; + } + + @Override + public ModelicaScope getContainingScope() { + return ((AbstractModelicaNode) parent).getMostSpecificScope(); + } + + @Override + public ModelicaScope getMostSpecificScope() { + if (ownScope == null) { + return getContainingScope(); + } else { + return ownScope; + } + } + + /** + * For resolver, set the lexical scope defined by this node, if any. + * + * @param scope Scope defined by this specific node + */ + void setOwnScope(ModelicaScope scope) { + ownScope = scope; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaRule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaRule.java new file mode 100644 index 0000000000..b0c00acf6b --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/AbstractModelicaRule.java @@ -0,0 +1,753 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import java.util.List; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule; +import net.sourceforge.pmd.lang.rule.AbstractRule; +import net.sourceforge.pmd.lang.rule.ImmutableLanguage; + +/** + * Base class for rules for Modelica language. + */ +public abstract class AbstractModelicaRule extends AbstractRule implements ModelicaParserVisitor, ImmutableLanguage { + public AbstractModelicaRule() { + super.setLanguage(LanguageRegistry.getLanguage(ModelicaLanguageModule.NAME)); + } + + @Override + public void apply(final List nodes, final RuleContext ctx) { + visitAll(nodes, ctx); + } + + protected void visitAll(final List nodes, final RuleContext ctx) { + for (final Object element : nodes) { + final ASTStoredDefinition node = (ASTStoredDefinition) element; + visit(node, ctx); + } + } + + @Override + public Object visit(AbstractModelicaNode node, Object data) { + for (int i = 0; i < node.jjtGetNumChildren(); ++i) { + node.jjtGetChild(i).jjtAccept(this, data); + } + return data; + } + + @Override + public Object visit(ASTNegated node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTStoredDefinition node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTWithinClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassDefinition node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEncapsulatedClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassPrefixes node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTPartialClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTModelClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRecordClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTOperatorRecordClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTBlockClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTConnectorClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTExpandableConnectorClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTTypeClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTPackageClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTPureClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTImpureClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTOperatorClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTOperator node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleLongClassSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTExtendingLongClassSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleShortClassSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEnumerationShortClassSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTDerClassSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTDerClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTBasePrefix node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEnumList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEnumerationLiteral node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComposition node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTExternalClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTLanguageSpecification node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTExternalFunctionCall node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRedeclareClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFinalClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTInnerClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTOuterClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTReplaceableClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRegularElement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTImportClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRenamingImportClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTUnqualifiedImportClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTMultipleDefinitionImportClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSingleDefinitionImportClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTImportList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTExtendsClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTConstrainingClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTTypePrefix node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFlowClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTStreamClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTDiscreteClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTParameterClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTConstantClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTInputClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTOutputClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTTypeSpecifier node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentDeclaration node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTConditionAttribute node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTDeclaration node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTLongModification node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTShortModification node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTAssignmentModification node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassModification node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTArgumentList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTArgument node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementModificationOrReplaceable node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEachClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementModification node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementRedeclaration node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementReplaceable node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentClause1 node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentDeclaration1 node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTShortClassDefinition node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEquationSection node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTInitialClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTAlgorithmSection node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEquation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRegularEquation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionCallEquation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTAssignmentStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionCallStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTAssignmentFromMultiResultFunctionCall node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTBreakStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTReturnStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfEquation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTThenClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElseIfClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElseClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTForEquation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTEquationList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTStatementList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTForStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTForIndices node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTForIndex node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhileStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhenEquation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhenClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTElseWhenClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhenStatement node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTConnectClause node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfExpression node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleExpression node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTLogicalExpression node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTLogicalTerm node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRelation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTRelOp node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTArithmeticExpression node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTAddOp node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTTerm node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTMulOp node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFactor node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFalseLiteral node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTTrueLiteral node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionInvocation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTListOfExpressionLists node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTNumberLiteral node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTStringLiteral node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTName node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleName node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSubscriptedName node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentReference node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionCallArgs node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionArguments node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTNamedArguments node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTNamedArgument node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionArgument node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTOutputExpressionList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTExpressionList node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTArraySubscripts node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTSubscript node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTColonSubsript node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTComment node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTStringComment node, Object data) { + return visit((AbstractModelicaNode) node, data); + } + + @Override + public Object visit(ASTAnnotation node, Object data) { + return visit((AbstractModelicaNode) node, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Helper.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Helper.java new file mode 100644 index 0000000000..871700b60a --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Helper.java @@ -0,0 +1,28 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import java.util.List; + +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolvableEntity; + +final class Helper { + private Helper() { + } + + // For Rule Designer + static String getResolvedTo(ResolutionResult result) { + List decls = result.getBestCandidates(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < decls.size(); ++i) { + if (i != 0) { + sb.append(", "); + } + sb.append(decls.get(i).getDescriptiveName()); + } + return sb.toString(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/InternalModelicaNodeApi.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/InternalModelicaNodeApi.java new file mode 100644 index 0000000000..e3aa208da8 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/InternalModelicaNodeApi.java @@ -0,0 +1,32 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassType; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionContext; +import net.sourceforge.pmd.lang.modelica.resolver.Watchdog; + +@InternalApi +public final class InternalModelicaNodeApi { + private InternalModelicaNodeApi() {} + + public static void setNodeOwnScope(ModelicaNode node, ModelicaScope scope) { + ((AbstractModelicaNode) node).setOwnScope(scope); + } + + public static boolean isQualifiedImport(ModelicaImportClause importClause) { + return ((AbstractModelicaImportClause) importClause).isQualified(); + } + + public static void resolveImportedSimpleName(ModelicaImportClause importClause, ResolutionContext result, String simpleName) throws Watchdog.CountdownException { + ((AbstractModelicaImportClause) importClause).resolveSimpleName(result, simpleName); + } + + public static void populateExtendsAndImports(ModelicaClassSpecifierNode classNode, ModelicaClassType classTypeDeclaration) { + ((AbstractModelicaClassSpecifierNode) classNode).populateExtendsAndImports(classTypeDeclaration); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaClassSpecifierNode.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaClassSpecifierNode.java new file mode 100644 index 0000000000..7a39f9902e --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaClassSpecifierNode.java @@ -0,0 +1,12 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +/** + * Public API for class defining AST nodes. + */ +public interface ModelicaClassSpecifierNode extends ModelicaNode { + String getSimpleClassName(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaImportClause.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaImportClause.java new file mode 100644 index 0000000000..454ab4ce69 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaImportClause.java @@ -0,0 +1,8 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public interface ModelicaImportClause { +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaNode.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaNode.java new file mode 100644 index 0000000000..54bb3fc2e4 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaNode.java @@ -0,0 +1,33 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaScope; + +/** + * Public interface for all Modelica AST nodes. + */ +public interface ModelicaNode extends Node { + /** + * Returns the lexical scope this node is contained in. + */ + ModelicaScope getContainingScope(); + + /** + * Returns the most specific lexical scope naturally associated with this node. + * + * @return the scope defined by this node itself or the same as {@link getContainingScope} otherwise + */ + ModelicaScope getMostSpecificScope(); + + Object jjtAccept(ModelicaParserVisitor visitor, Object data); + + @Override + ModelicaNode jjtGetParent(); + + @Override + ModelicaNode jjtGetChild(int index); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParserVisitorAdapter.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParserVisitorAdapter.java new file mode 100644 index 0000000000..8ff1b60385 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ModelicaParserVisitorAdapter.java @@ -0,0 +1,729 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public class ModelicaParserVisitorAdapter implements ModelicaParserVisitor { + public Object visit(ModelicaNode node, Object data) { + for (int i = 0; i < node.jjtGetNumChildren(); ++i) { + node.jjtGetChild(i).jjtAccept(this, data); + } + return data; + } + + @Override + public Object visit(AbstractModelicaNode node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTNegated node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTStoredDefinition node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTWithinClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassDefinition node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEncapsulatedClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassPrefixes node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTPartialClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTModelClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRecordClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTOperatorRecordClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTBlockClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTConnectorClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTExpandableConnectorClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTTypeClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTPackageClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTPureClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTImpureClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTOperatorClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTOperator node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleLongClassSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTExtendingLongClassSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleShortClassSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEnumerationShortClassSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTDerClassSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTDerClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTBasePrefix node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEnumList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEnumerationLiteral node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComposition node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTExternalClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTLanguageSpecification node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTExternalFunctionCall node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRedeclareClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFinalClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTInnerClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTOuterClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTReplaceableClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRegularElement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTImportClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRenamingImportClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTUnqualifiedImportClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTMultipleDefinitionImportClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSingleDefinitionImportClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTImportList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTExtendsClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTConstrainingClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTTypePrefix node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFlowClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTStreamClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTDiscreteClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTParameterClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTConstantClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTInputClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTOutputClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTTypeSpecifier node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentDeclaration node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTConditionAttribute node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTDeclaration node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTLongModification node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTShortModification node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTAssignmentModification node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTClassModification node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTArgumentList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTArgument node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementModificationOrReplaceable node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEachClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementModification node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementRedeclaration node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElementReplaceable node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentClause1 node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentDeclaration1 node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTShortClassDefinition node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEquationSection node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTInitialClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTAlgorithmSection node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEquation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRegularEquation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionCallEquation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTAssignmentStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionCallStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTAssignmentFromMultiResultFunctionCall node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTBreakStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTReturnStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfEquation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTThenClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElseIfClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElseClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTForEquation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTEquationList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTStatementList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTForStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTForIndices node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTForIndex node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhileStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhenEquation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhenClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTElseWhenClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTWhenStatement node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTConnectClause node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTIfExpression node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleExpression node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTLogicalExpression node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTLogicalTerm node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRelation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTRelOp node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTArithmeticExpression node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTAddOp node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTTerm node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTMulOp node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFactor node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFalseLiteral node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTTrueLiteral node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionInvocation node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTListOfExpressionLists node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTNumberLiteral node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTStringLiteral node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTName node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSimpleName node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSubscriptedName node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComponentReference node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionCallArgs node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionArguments node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTNamedArguments node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTNamedArgument node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTFunctionArgument node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTOutputExpressionList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTExpressionList node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTArraySubscripts node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTSubscript node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTColonSubsript node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTComment node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTStringComment node, Object data) { + return visit((ModelicaNode) node, data); + } + + @Override + public Object visit(ASTAnnotation node, Object data) { + return visit((ModelicaNode) node, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ResolvableModelicaNode.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ResolvableModelicaNode.java new file mode 100644 index 0000000000..a5945cc31a --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/ResolvableModelicaNode.java @@ -0,0 +1,17 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; + +/** + * A public API for resolving lexical references to class or components. + */ +public interface ResolvableModelicaNode { + /** + * Tries to resolve the declaration of the referenced component. + */ + ResolutionResult getResolutionCandidates(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Visibility.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Visibility.java new file mode 100644 index 0000000000..3e876b39b5 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ast/Visibility.java @@ -0,0 +1,12 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.ast; + +public enum Visibility { + UNSPEC(), + PRIVATE(), + PROTECTED(), + PUBLIC() +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaDeclaration.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaDeclaration.java new file mode 100644 index 0000000000..41e9246190 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaDeclaration.java @@ -0,0 +1,19 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * Internal base class for Modelica declarations, see ${@link ModelicaDeclaration} for public API. + */ +abstract class AbstractModelicaDeclaration implements ModelicaDeclaration { + /** + * Resolves further name components, supposing previous ones resolved to this declaration. + * + * @param result Where to place resolution results + * @param name Further name parts to resolve + * @throws Watchdog.CountdownException if too many resolution steps were performed + */ + abstract void resolveFurtherNameComponents(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException; +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaScope.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaScope.java new file mode 100644 index 0000000000..2efb90bb89 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/AbstractModelicaScope.java @@ -0,0 +1,90 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Internal base class for Modelica lexical scopes, see {@link ModelicaScope} for the public API. + */ +abstract class AbstractModelicaScope implements ModelicaScope { + private AbstractModelicaScope parent; + private final List declarations = new ArrayList<>(); + private final Map> declarationsByName = new HashMap<>(); + + void setParent(AbstractModelicaScope scope) { + parent = scope; + } + + @Override + public ModelicaScope getParent() { + return parent; + } + + void addDeclaration(ModelicaDeclaration declaration) { + String name = declaration.getSimpleDeclarationName(); + declarations.add(declaration); + if (!declarationsByName.containsKey(name)) { + declarationsByName.put(name, new ArrayList()); + } + declarationsByName.get(name).add(declaration); + } + + @Override + public List getContainedDeclarations() { + return Collections.unmodifiableList(declarations); + } + + List getDirectlyDeclared(String simpleName) { + List result = declarationsByName.get(simpleName); + if (result != null) { + return result; + } else { + return Collections.emptyList(); + } + } + + /** + * Resolves a name as if it is written inside this lexical scope in a file. + */ + abstract void resolveLexically(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException; + + @Override + public ResolutionResult safeResolveLexically(Class clazz, ResolutionState state, CompositeName name) { + ResolutionContext result = state.createContext(); + try { + resolveLexically(result, name); + } catch (Watchdog.CountdownException e) { + result.markTtlExceeded(); + } + return result.get(clazz); + } + + protected abstract String getRepresentation(); + + // For testing purposes + String getNestingRepresentation() { + ModelicaScope parentScope = getParent(); + String prefix = ""; + if (parentScope != null) { + prefix = ((AbstractModelicaScope) parentScope).getNestingRepresentation(); + } + return prefix + "#" + getRepresentation(); + } + + @Override + public RootScope getRoot() { + return getParent().getRoot(); + } + + @Override + public String toString() { + return getRepresentation(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/CompositeName.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/CompositeName.java new file mode 100644 index 0000000000..b73497d5ef --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/CompositeName.java @@ -0,0 +1,86 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * An immutable composite name representation for use in "pattern matching style". + * + * Supports lightweight splitting into first element and "tail" (everything else). + */ +public final class CompositeName { + private static final CompositeName NIL = new CompositeName(null, null); + static final String ROOT_PSEUDO_NAME = ""; + public static final String NAME_COMPONENT_SEPARATOR = "."; + + private String head; + private CompositeName tail; + + private CompositeName(String head, CompositeName tail) { + this.head = head; + this.tail = tail; + } + + private static CompositeName create(String[] components, int startIndex, int total, boolean isAbsolute) { + if (isAbsolute) { + return new CompositeName(ROOT_PSEUDO_NAME, create(components, startIndex, total, false)); + } else if (startIndex == total) { + return NIL; + } else { + return new CompositeName(components[startIndex], create(components, startIndex + 1, total, false)); + } + } + + public static CompositeName create(boolean isAbsolute, String[] components) { + return create(components, 0, components.length, isAbsolute); + } + + public static CompositeName create(boolean isAbsolute, String[] componenets, int prefixLength) { + return create(componenets, 0, prefixLength, isAbsolute); + } + + public static CompositeName create(String simpleName) { + return new CompositeName(simpleName, NIL); + } + + public boolean isEmpty() { + return head == null; + } + + public String getHead() { + return head; + } + + public CompositeName getTail() { + return tail; + } + + private CompositeName matchPrefix(String[] prefix, int currentIndex) { + if (currentIndex == prefix.length) { + return this; + } else if (prefix[currentIndex].equals(head) && tail != null) { + return tail.matchPrefix(prefix, currentIndex + 1); + } else { + return null; + } + } + + /** + * Tries to match the prefix argument with the first elements of this name + * + * @returns the remaining elements on success or null on failure + */ + public CompositeName matchPrefix(String[] prefix) { + return matchPrefix(prefix, 0); + } + + @Override + public String toString() { + if (isEmpty()) { + return "Nil"; + } else { + return head + "::" + tail.toString(); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/InternalModelicaResolverApi.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/InternalModelicaResolverApi.java new file mode 100644 index 0000000000..71ef053e35 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/InternalModelicaResolverApi.java @@ -0,0 +1,26 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaImportClause; +import net.sourceforge.pmd.lang.modelica.ast.Visibility; + +@InternalApi +public final class InternalModelicaResolverApi { + private InternalModelicaResolverApi() {} + + public static void addImportToClass(ModelicaClassType classTypeDeclaration, Visibility visibility, ModelicaImportClause clause) { + ((ModelicaClassDeclaration) classTypeDeclaration).addImport(visibility, clause); + } + + public static void addExtendToClass(ModelicaClassType classTypeDeclaration, Visibility visibility, CompositeName extendedClass) { + ((ModelicaClassDeclaration) classTypeDeclaration).addExtends(visibility, extendedClass); + } + + public static void resolveFurtherNameComponents(ModelicaDeclaration declaration, ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + ((AbstractModelicaDeclaration) declaration).resolveFurtherNameComponents(result, name); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaBuiltinType.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaBuiltinType.java new file mode 100644 index 0000000000..acff170396 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaBuiltinType.java @@ -0,0 +1,59 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * Built-in Modelica types. There are only four basic variants: Boolean, Integer, + * Real and String but they can have modifications applied (such as min/max values), + * so do not introduce them as singletones for extendability in the future. + */ +public class ModelicaBuiltinType implements ModelicaType { + public enum BaseType { + BOOLEAN("Boolean"), + INTEGER("Integer"), + REAL("Real"), + STRING("String"); + + private final String name; + + BaseType(String name) { + this.name = name; + } + + String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + } + + private final BaseType baseType; + + ModelicaBuiltinType(BaseType tpe) { + baseType = tpe; + } + + public BaseType getBaseType() { + return baseType; + } + + @Override + public String getSimpleTypeName() { + return baseType.toString(); + } + + @Override + public String getFullTypeName() { + return baseType.toString(); + } + + @Override + public String getDescriptiveName() { + return getFullTypeName(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassDeclaration.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassDeclaration.java new file mode 100644 index 0000000000..962c67f0e5 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassDeclaration.java @@ -0,0 +1,279 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.ArrayList; +import java.util.List; + +import net.sourceforge.pmd.lang.modelica.ast.ASTClassDefinition; +import net.sourceforge.pmd.lang.modelica.ast.InternalModelicaNodeApi; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaClassSpecifierNode; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaImportClause; +import net.sourceforge.pmd.lang.modelica.ast.Visibility; + +/** + * Internal representation of a declared Modelica class, see {@link ModelicaClassType} for public API. + */ +class ModelicaClassDeclaration extends AbstractModelicaDeclaration implements ModelicaClassType { + private ModelicaClassScope ownScope; + private boolean encapsulated; + private boolean partial; + private ModelicaClassSpecialization specialization; + private String simpleName; + private final List imports = new ArrayList<>(); + private final List extendedClasses = new ArrayList<>(); + private List resolvedExtends; + + ModelicaClassDeclaration(ASTClassDefinition node) { + encapsulated = node.isEncapsulated(); + partial = node.isPartial(); + specialization = node.getSpecialization(); + ModelicaClassSpecifierNode classNode = node.getClassSpecifier(); + simpleName = classNode.getSimpleClassName(); + InternalModelicaNodeApi.populateExtendsAndImports(classNode, this); + } + + /** + * To be called by a corresponding AST node describing itself + */ + void addImport(Visibility visibility, ModelicaImportClause clause) { + // TODO handle visibility + imports.add(clause); + } + + /** + * To be called by a corresponding AST node describing itself + */ + void addExtends(Visibility visibility, CompositeName extendedClass) { + // TODO handle visibility + assert resolvedExtends == null; + extendedClasses.add(extendedClass); + } + + private List getResolvedExtends(ResolutionState lazyInitState) { + if (resolvedExtends == null) { + ResolutionContext ctx = lazyInitState.createContext(); + try { + for (CompositeName name : extendedClasses) { + ctx.watchdogTick(); + ((AbstractModelicaScope) ownScope.getParent()).resolveLexically(ctx, name); + } + } catch (Watchdog.CountdownException e) { + ctx.markTtlExceeded(); + } + resolvedExtends = new ArrayList<>(); + for (ModelicaType decl: ctx.getTypes().getBestCandidates()) { + if (decl instanceof ModelicaClassDeclaration) { + resolvedExtends.add(((ModelicaClassDeclaration) decl).getClassScope()); + } + } + } + return resolvedExtends; + } + + @Override + public ResolutionResult safeResolveComponent(Class clazz, ResolutionState state, CompositeName name) { + ResolutionContext result = state.createContext(); + try { + lookupInInstanceScope(result, name); + } catch (Watchdog.CountdownException e) { + result.markTtlExceeded(); + } + return result.get(clazz); + } + + /** + * Looks up the first part of composite name in imported classes (either qualified or unqualified) + * + * @param state resolution parameters + * @param firstName a name to resolve + * @param qualified whether we are looking at qualified imports or unqualified ones + * @return List of candidate resolutions + * @throws Watchdog.CountdownException if too many lookup steps were performed + */ + private ResolutionResult lookupImported(ResolutionState state, String firstName, boolean qualified) throws Watchdog.CountdownException { + state.tick(); + + ResolutionContext result = state.createContext(); + for (final ModelicaImportClause importClause: imports) { + ResolutionContext subResult = state.createContext(); + if (InternalModelicaNodeApi.isQualifiedImport(importClause) == qualified) { + InternalModelicaNodeApi.resolveImportedSimpleName(importClause, subResult, firstName); + } + result.accumulate(subResult.getDeclaration()); + } + return result.getDeclaration(); + } + + /** + * Look up composite name inside this instance scope (and not above). + * This method itself implements corresponding part of "5.3.1 Simple Name Lookup" of MLS 3.4. + * + * @param result an object to place results to + * @param name a name to look up + * @throws Watchdog.CountdownException in too many lookup steps were performed + */ + void lookupInInstanceScope(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + if (name.isEmpty()) { + result.addCandidate(this); + return; + } + + String firstName = name.getHead(); + CompositeName furtherParts = name.getTail(); + + result.watchdogTick(); + + // Otherwise, lookup... + // ... among declared names of the class + for (ModelicaDeclaration decl: ownScope.getDirectlyDeclared(firstName)) { + lookupInInstanceScopeFurtherParts(result, decl, furtherParts); + } + result.markHidingPoint(); + // ... and from inherited, too + for (ModelicaClassScope extendedClass: getResolvedExtends(result.getState())) { + for (ModelicaDeclaration inheritedDecl: extendedClass.getDirectlyDeclared(firstName)) { + lookupInInstanceScopeFurtherParts(result, inheritedDecl, furtherParts); + } + } + result.markHidingPoint(); + // ... using qualified imports + ResolutionResult qualifiedImports = lookupImported(result.getState(), firstName, true); + for (ModelicaDeclaration importedDecl: qualifiedImports.getBestCandidates()) { + lookupInInstanceScopeFurtherParts(result, importedDecl, furtherParts); + } + result.markHidingPoint(); + for (ModelicaDeclaration importedDecl: qualifiedImports.getHiddenCandidates()) { + lookupInInstanceScopeFurtherParts(result, importedDecl, furtherParts); + } + result.markHidingPoint(); + // ... then using unqualified imports + ResolutionResult unqualifiedImports = lookupImported(result.getState(), firstName, false); + for (ModelicaDeclaration importedDecl: unqualifiedImports.getBestCandidates()) { + lookupInInstanceScopeFurtherParts(result, importedDecl, furtherParts); + } + result.markHidingPoint(); + for (ModelicaDeclaration importedDecl: unqualifiedImports.getHiddenCandidates()) { + lookupInInstanceScopeFurtherParts(result, importedDecl, furtherParts); + } + } + + /** + * Recurse into the first resolved element of composite name + * + * This method itself implements the "5.3.2 Composite Name Lookup" of MLS 3.4 with the first step + * being made by `lookupInInstanceScope`. + * + * @param result an object to place results to + * @param resolvedSimpleName a declaration found when resolving the very first part of composite name + * @param furtherParts an unresolved "tail" of a composite name + * @throws Watchdog.CountdownException if too many resolution steps were performed + */ + private void lookupInInstanceScopeFurtherParts(ResolutionContext result, ModelicaDeclaration resolvedSimpleName, CompositeName furtherParts) throws Watchdog.CountdownException { + result.watchdogTick(); + + if (furtherParts.isEmpty()) { + result.addCandidate(resolvedSimpleName); + return; + } + + if (resolvedSimpleName instanceof ModelicaComponentDeclaration) { + ModelicaComponentDeclaration component = (ModelicaComponentDeclaration) resolvedSimpleName; + if (result.getState().needRecurseInto(component)) { + ResolutionResult componentTypes = component.getTypeCandidates(); + for (ModelicaType tpe : componentTypes.getBestCandidates()) { + if (tpe instanceof ModelicaClassDeclaration) { + ((ModelicaClassDeclaration) tpe).lookupInInstanceScope(result, furtherParts); + } + } + result.markHidingPoint(); + for (ModelicaType tpe : componentTypes.getHiddenCandidates()) { + if (tpe instanceof ModelicaClassDeclaration) { + ((ModelicaClassDeclaration) tpe).lookupInInstanceScope(result, furtherParts); + } + } + } + } else if (resolvedSimpleName instanceof ModelicaClassDeclaration) { + ModelicaClassDeclaration classDecl = (ModelicaClassDeclaration) resolvedSimpleName; + classDecl.lookupInInstanceScope(result, furtherParts); + } else { + throw new IllegalArgumentException("Can recurse into class or component only"); + } + } + + void setOwnScope(ModelicaClassScope scope) { + ownScope = scope; + } + + @Override + public ModelicaClassSpecialization getSpecialization() { + return specialization; + } + + @Override + public boolean isConnectorLike() { + return specialization == ModelicaClassSpecialization.CONNECTOR || specialization == ModelicaClassSpecialization.EXPANDABLE_CONNECTOR; + } + + @Override + public boolean isEncapsulated() { + return encapsulated; + } + + @Override + public boolean isPartial() { + return partial; + } + + @Override + public ModelicaScope getContainingScope() { + return ownScope.getParent(); + } + + @Override + public ModelicaClassScope getClassScope() { + return ownScope; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (encapsulated) { + sb.append("encapsulated "); + } + if (partial) { + sb.append("partial "); + } + sb.append(specialization.toString()); + sb.append(' '); + sb.append(simpleName); + return sb.toString(); + } + + @Override + void resolveFurtherNameComponents(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + lookupInInstanceScope(result, name); + } + + @Override + public String getSimpleDeclarationName() { + return simpleName; + } + + @Override + public String getSimpleTypeName() { + return simpleName; + } + + @Override + public String getFullTypeName() { + return ownScope.getFullyQualifiedClassName(); + } + + @Override + public String getDescriptiveName() { + return getFullTypeName(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassScope.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassScope.java new file mode 100644 index 0000000000..6957a627cf --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassScope.java @@ -0,0 +1,44 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * A lexical scope corresponding to a Modelica class. + */ +public final class ModelicaClassScope extends AbstractModelicaScope { + private final ModelicaClassDeclaration classDeclaration; + + ModelicaClassScope(ModelicaClassDeclaration declaration) { + classDeclaration = declaration; + classDeclaration.setOwnScope(this); + } + + public ModelicaClassType getClassDeclaration() { + return classDeclaration; + } + + @Override + public void resolveLexically(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + InternalModelicaResolverApi.resolveFurtherNameComponents(classDeclaration, result, name); + if (classDeclaration.isEncapsulated()) { + getRoot().resolveBuiltin(result, name); + } else { + ((AbstractModelicaScope) getParent()).resolveLexically(result, name); + } + } + + @Override + public String getRepresentation() { + return "Class:" + classDeclaration.getSimpleTypeName(); + } + + String getFullyQualifiedClassName() { + if (getParent() instanceof ModelicaClassScope) { + return ((ModelicaClassScope) getParent()).getFullyQualifiedClassName() + "." + classDeclaration.getSimpleTypeName(); + } else { + return ((ModelicaSourceFileScope) getParent()).getFileFQCN(); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassSpecialization.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassSpecialization.java new file mode 100644 index 0000000000..71c177b27a --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassSpecialization.java @@ -0,0 +1,47 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * Enumerates "specialized kinds of classes" (package, model, connector, etc.) that define + * some restrictions and enhancements on what can be defined inside and how can they be used. + * + * See "4.6 Specialized classes" from MLS 3.4 + */ +public enum ModelicaClassSpecialization { + CLASS("class"), + MODEL("model"), + RECORD("record"), + OPERATOR_RECORD("operator record"), + BLOCK("block"), + CONNECTOR("connector"), + EXPANDABLE_CONNECTOR("expandable connector"), + TYPE("type"), + PACKAGE("package"), + FUNCTION("function"), + PURE_FUNCTION("pure function"), + OPERATOR_FUNCTION("operator function"), + PURE_OPERATOR_FUNCTION("pure operator function"), + OPERATOR("operator"); + + private String name; + + ModelicaClassSpecialization(String name) { + this.name = name; + } + + public static ModelicaClassSpecialization getFunctionSpecialization(boolean isPure, boolean isOperator) { + if (isPure) { + return isOperator ? PURE_OPERATOR_FUNCTION : PURE_FUNCTION; + } else { + return isOperator ? OPERATOR_FUNCTION : FUNCTION; + } + } + + @Override + public String toString() { + return name; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassType.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassType.java new file mode 100644 index 0000000000..e47c69f7f6 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaClassType.java @@ -0,0 +1,35 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * A Modelica type that is defined as a class (i.e., class, package, model, etc.). + */ +public interface ModelicaClassType extends ModelicaType, ModelicaDeclaration, SubcomponentResolver { + /** + * Returns the class specialization (i.e., package, model, function, etc.) + */ + ModelicaClassSpecialization getSpecialization(); + + /** + * Returns whether this class is some kind of connector. + */ + boolean isConnectorLike(); + + /** + * Returns whether this class is encapsulated. + */ + boolean isEncapsulated(); + + /** + * Returns whether this class is partial. + */ + boolean isPartial(); + + /** + * Returns the scope defined by this class itself. + */ + ModelicaClassScope getClassScope(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaComponentDeclaration.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaComponentDeclaration.java new file mode 100644 index 0000000000..75c8f38e4c --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaComponentDeclaration.java @@ -0,0 +1,218 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import net.sourceforge.pmd.lang.modelica.ast.ASTComponentClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTComponentDeclaration; +import net.sourceforge.pmd.lang.modelica.ast.ASTConditionAttribute; +import net.sourceforge.pmd.lang.modelica.ast.ASTConstantClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTDeclaration; +import net.sourceforge.pmd.lang.modelica.ast.ASTDiscreteClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTFlowClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTInputClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTName; +import net.sourceforge.pmd.lang.modelica.ast.ASTOutputClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTParameterClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTSimpleName; +import net.sourceforge.pmd.lang.modelica.ast.ASTStreamClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTTypePrefix; +import net.sourceforge.pmd.lang.modelica.ast.ASTTypeSpecifier; + +public class ModelicaComponentDeclaration extends AbstractModelicaDeclaration implements ModelicaDeclaration { + public enum ComponentKind { + FLOW("flow"), + STREAM("stream"), + NOTHING_SPECIAL(""); + + private String name; + + ComponentKind(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + public enum ComponentVariability { + DISCRETE("discrete"), + PARAMETER("parameter"), + CONSTANT("constant"), + CONTINUOUS("continuous"); + + private String name; + + ComponentVariability(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + public enum ComponentCausality { + INPUT("input"), + OUTPUT("output"), + ACAUSAL("acausal"); + + private String name; + + ComponentCausality(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + private ModelicaClassScope containingScope; + private ComponentKind kind; + private ComponentVariability variability; + private ComponentCausality causality; + private final ASTName typeName; + private ResolutionResult typeCandidates; + private final String declarationName; + private final ASTConditionAttribute condition; + + public ModelicaComponentDeclaration(ASTComponentDeclaration node) { + declarationName = node.getFirstChildOfType(ASTDeclaration.class).getFirstChildOfType(ASTSimpleName.class).getImage(); + condition = node.getFirstChildOfType(ASTConditionAttribute.class); + ASTComponentClause declarationRoot = node.getFirstParentOfType(ASTComponentClause.class); + ASTTypePrefix prefixes = declarationRoot.getFirstChildOfType(ASTTypePrefix.class); + parseTypePrefix(prefixes); + typeName = declarationRoot + .getFirstChildOfType(ASTTypeSpecifier.class) + .getFirstChildOfType(ASTName.class); + } + + void setContainingScope(ModelicaClassScope scope) { + containingScope = scope; + } + + @Override + public ModelicaClassScope getContainingScope() { + return containingScope; + } + + private void parseTypePrefix(ASTTypePrefix prefix) { + if (prefix.getFirstChildOfType(ASTFlowClause.class) != null) { + kind = ComponentKind.FLOW; + } else if (prefix.getFirstChildOfType(ASTStreamClause.class) != null) { + kind = ComponentKind.STREAM; + } else { + kind = ComponentKind.NOTHING_SPECIAL; + } + + if (prefix.getFirstChildOfType(ASTDiscreteClause.class) != null) { + variability = ComponentVariability.DISCRETE; + } else if (prefix.getFirstChildOfType(ASTParameterClause.class) != null) { + variability = ComponentVariability.PARAMETER; + } else if (prefix.getFirstChildOfType(ASTConstantClause.class) != null) { + variability = ComponentVariability.CONSTANT; + } else { + variability = ComponentVariability.CONTINUOUS; + } + + if (prefix.getFirstChildOfType(ASTInputClause.class) != null) { + causality = ComponentCausality.INPUT; + } else if (prefix.getFirstChildOfType(ASTOutputClause.class) != null) { + causality = ComponentCausality.OUTPUT; + } else { + causality = ComponentCausality.ACAUSAL; + } + } + + public ASTConditionAttribute getCondition() { + return condition; + } + + /** + * Whether this component is declared as flow, stream or nothing special. + */ + public ComponentKind getKind() { + return kind; + } + + /** + * Whether this component is a constant, a parameter, a discrete or a continuous variable. + */ + public ComponentVariability getVariability() { + return variability; + } + + /** + * Whether this component is input, output or acausal. + */ + public ComponentCausality getCausality() { + return causality; + } + + @Override + public String getSimpleDeclarationName() { + return declarationName; + } + + @Override + public String getDescriptiveName() { + return declarationName; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (kind != null) { + sb.append(kind.toString()); + sb.append(' '); + } + if (variability != null) { + sb.append(variability.toString()); + sb.append(' '); + } + if (causality != null) { + sb.append(causality.toString()); + sb.append(' '); + } + sb.append(typeName); + sb.append(' '); + sb.append(declarationName); + return sb.toString(); + } + + public ResolutionResult getTypeCandidates() { + if (typeCandidates == null) { + ResolutionContext ctx = ResolutionState.forComponentReference().createContext(); + try { + getContainingScope().resolveLexically(ctx, typeName.getCompositeName()); + } catch (Watchdog.CountdownException e) { + ctx.markTtlExceeded(); + } + typeCandidates = ctx.getTypes(); + } + return typeCandidates; + } + + @Override + void resolveFurtherNameComponents(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + if (name.isEmpty()) { + result.addCandidate(this); + return; + } + + ResolutionResult resolvedType = getTypeCandidates(); + for (ModelicaType decl: resolvedType.getBestCandidates()) { + ((AbstractModelicaDeclaration) decl).resolveFurtherNameComponents(result, name); + } + result.markHidingPoint(); + for (ModelicaType decl: resolvedType.getHiddenCandidates()) { + ((AbstractModelicaDeclaration) decl).resolveFurtherNameComponents(result, name); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaDeclaration.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaDeclaration.java new file mode 100644 index 0000000000..9808542d3c --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaDeclaration.java @@ -0,0 +1,20 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * Some Modelica entity that is explicitly declared with some name inside some lexical scope. + */ +public interface ModelicaDeclaration extends ResolvableEntity { + /** + * Returns the name of a declaration, such as "RealInput". + */ + String getSimpleDeclarationName(); + + /** + * Returns the scope in which this symbol is declared. + */ + ModelicaScope getContainingScope(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaScope.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaScope.java new file mode 100644 index 0000000000..db633846d9 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaScope.java @@ -0,0 +1,34 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.List; + +/** + * A lexical scope of Modelica code. + * That is, a component declaration does not have one, it is its type that does (but these may be resolved to multiple + * classes or not resolved at all, these classes generally reside in other files, etc.) + * + * Please do not confuse this with {@link SubcomponentResolver} that represents "view from the outside" on something + * possibly looked up from other file via component reference. + */ +public interface ModelicaScope { + /** + * Returns the declarations that were lexically declared in this scope. + */ + List getContainedDeclarations(); + + /** + * Resolves a name as if it is written inside this lexical scope in a file. + */ + ResolutionResult safeResolveLexically(Class clazz, ResolutionState state, CompositeName name); + + /** + * Returns the parent (i.e., containing) scope. + */ + ModelicaScope getParent(); + + RootScope getRoot(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSourceFileScope.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSourceFileScope.java new file mode 100644 index 0000000000..48bbc27158 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSourceFileScope.java @@ -0,0 +1,79 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; + +/** + * A scope corresponding to some specific Modelica source code file. + */ +public final class ModelicaSourceFileScope extends AbstractModelicaScope { + private final String fileFQCN; + private final String[] packageComponents; + + ModelicaSourceFileScope(ASTStoredDefinition node) { + fileFQCN = node.getImage(); + if (fileFQCN.isEmpty()) { + packageComponents = new String[0]; + } else { + packageComponents = fileFQCN.split("\\."); + } + } + + /** + * Resolve name as if not accounting for fileFQCN (as if it would be empty). + */ + void lookupLocally(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + if (name.isEmpty()) { + // TODO technically, here we should return an implicit package... + return; + } + result.watchdogTick(); + + String firstName = name.getHead(); + CompositeName furtherNames = name.getTail(); + + for (ModelicaDeclaration decl: getDirectlyDeclared(firstName)) { + ResolutionContext tmpContext = result.getState().createContext(); + ((ModelicaClassDeclaration) decl).lookupInInstanceScope(result, furtherNames); + // According to "5.2 Enclosing classes" from MLS 3.4, the order of definitions inside the unnamed + // enclosing class is unspecified, so handle name hiding with care. + result.accumulate(tmpContext.get(ResolvableEntity.class)); + } + } + + /** + * Resolve name accounting for fileFQCN. To be called from {@link RootScope}. + */ + void lookupGlobally(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + result.watchdogTick(); + CompositeName remainingNameComponents = name.matchPrefix(packageComponents); + if (remainingNameComponents != null) { + lookupLocally(result, remainingNameComponents); + } + } + + @Override + public void resolveLexically(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + if (!isInDefaultPackage()) { + // otherwise we would get it from the RootScope anyway + lookupLocally(result, name); + } + ((AbstractModelicaScope) getParent()).resolveLexically(result, name); + } + + @Override + public String getRepresentation() { + return "FILE"; + } + + public boolean isInDefaultPackage() { + return fileFQCN.isEmpty(); + } + + public String getFileFQCN() { + return fileFQCN; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java new file mode 100644 index 0000000000..ed4df5377d --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaSymbolFacade.java @@ -0,0 +1,14 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; + +public class ModelicaSymbolFacade { + public void initializeWith(ASTStoredDefinition node) { + ScopeAndDeclarationFinder sc = new ScopeAndDeclarationFinder(); + node.jjtAccept(sc, null); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaType.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaType.java new file mode 100644 index 0000000000..0078e718a2 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaType.java @@ -0,0 +1,20 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * Some Modelica type (either class or built-in type) that some component may have. + */ +public interface ModelicaType extends ResolvableEntity { + /** + * Returns short name of a type, such as "Real" or "Filter". + */ + String getSimpleTypeName(); + + /** + * Returns the fully-qualified name, when appropriate, or simple name for primitive types. + */ + String getFullTypeName(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionContext.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionContext.java new file mode 100644 index 0000000000..15547b0efb --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionContext.java @@ -0,0 +1,140 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sourceforge.pmd.annotation.InternalApi; + +@InternalApi +public class ResolutionContext { + private final ResolutionState state; + private final List bestCandidates = new ArrayList<>(); + private final List hiddenCandidates = new ArrayList<>(); + private boolean ttlExceeded = false; + private boolean isCollectingHidden = false; + + ResolutionContext(ResolutionState ctx) { + state = ctx; + } + + public ResolutionState getState() { + return state; + } + + public void watchdogTick() throws Watchdog.CountdownException { + state.tick(); + } + + public void addCandidate(ResolvableEntity candidate) { + if (isCollectingHidden) { + hiddenCandidates.add(candidate); + } else { + bestCandidates.add(candidate); + } + } + + /** + * Marks the corresponding resolution process as timed out. + * + * Usually, this method is called after catching `Watchdog.CountdownException`. + */ + void markTtlExceeded() { + ttlExceeded = true; + } + + /** + * Mark previously resolved declarations (if any) as more important than the subsequent ones. + * + * It is correct to call this method even at the point when nothing is resolved yet. + * If there is something resolved so far, the subsequent declarations will be considered as hidden. + * If there is nothing resolved so far, the call is ignored. + */ + public void markHidingPoint() { + if (!bestCandidates.isEmpty()) { + isCollectingHidden = true; + } + } + + void accumulate(ResolutionResult result) { + bestCandidates.addAll(result.getBestCandidates()); + hiddenCandidates.addAll(result.getHiddenCandidates()); + } + + private static class Result implements ResolutionResult { + private final List bestCandidates = new ArrayList<>(); + private final List hiddenCandidates = new ArrayList<>(); + private final boolean timedOut; + + Result(Class tpe, List best, List hidden, boolean timedOut) { + for (Object b: best) { + if (tpe.isInstance(b)) { + bestCandidates.add((A) b); + } + } + for (Object h: hidden) { + if (tpe.isInstance(h)) { + hiddenCandidates.add((A) h); + } + } + this.timedOut = timedOut; + } + + @Override + public List getBestCandidates() { + return Collections.unmodifiableList(bestCandidates); + } + + @Override + public List getHiddenCandidates() { + return Collections.unmodifiableList(hiddenCandidates); + } + + @Override + public boolean isUnresolved() { + return bestCandidates.isEmpty(); + } + + @Override + public boolean isClashed() { + return bestCandidates.size() > 1; + } + + @Override + public boolean hasHiddenResults() { + return !hiddenCandidates.isEmpty(); + } + + @Override + public boolean wasTimedOut() { + return timedOut; + } + } + + public ResolutionResult getTypes() { + return new Result<>(ModelicaType.class, bestCandidates, hiddenCandidates, ttlExceeded); + } + + public ResolutionResult getDeclaration() { + return new Result<>(ModelicaDeclaration.class, bestCandidates, hiddenCandidates, ttlExceeded); + } + + public ResolutionResult get(Class clazz) { + return new Result<>(clazz, bestCandidates, hiddenCandidates, ttlExceeded); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Resolved["); + sb.append(bestCandidates.size()); + sb.append('/'); + sb.append(hiddenCandidates.size()); + sb.append(']'); + return sb.toString(); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionResult.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionResult.java new file mode 100644 index 0000000000..1af6551e37 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionResult.java @@ -0,0 +1,66 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.List; + +/** + * This class represents a resolution result of some (possibly composite) name inside some context + * + * Usage of special interface instead of plain List<ModelicaDeclaration> allows returning some additional information + * such as "layers" of resolved symbol (for example, these 10 symbols are obviously hidden, but these two are equally + * relevant, thus introducing name clash). Moreover, more diagnostic information can be introduced in the future + * without changing lots of source code. + */ +public interface ResolutionResult { + /** + * Returns declarations that are supposed to be taken by the Modelica compiler (normally, exactly one match). + * + * @return A declaration(s) to be used. Every result not consisting of exactly one declaration signifies + * a possible error-severity issue in the source to be analysed. + */ + List getBestCandidates(); + + /** + * Returns declarations that are definitely hidden by some others. + * + * Non empty returned collection may signify either warning-grade issue in the source to be analysed + * or just some peculiarities of the particular Modelica code design, it depends. + * + * @return A collections of definitely hidden resolution candidates + */ + List getHiddenCandidates(); + + /** + * Whether any resolution candidate exists. + * + * @return getBestCandidates().size != 0 + */ + boolean isUnresolved(); + + /** + * Whether the symbol resolver found multiple equally relevant resolution candidates. + * + * @return getBestCandidates().size > 1 + */ + boolean isClashed(); + + /** + * Whether the symbol resolver found any symbol suspiciously hidden by other ones. + * + * @return getHiddenCandidates().size != 0 + */ + boolean hasHiddenResults(); + + /** + * During resolution, loops may occur. This leads to timing out the resolution and (possibly) partial results. + * + * This method may return `true` even if other ones return some non-empty result. It merely signifies that + * the results may depend on particular limit on the number of resolution steps. + * + * @return Whether resolution was timed out + */ + boolean wasTimedOut(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionState.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionState.java new file mode 100644 index 0000000000..2b01ec1e8c --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolutionState.java @@ -0,0 +1,35 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +public final class ResolutionState { + private final Watchdog watchdog; + private boolean constantsOnly; + + private ResolutionState(boolean constantsOnly) { + watchdog = new Watchdog(1000); + this.constantsOnly = constantsOnly; + } + + public static ResolutionState forType() { + return new ResolutionState(true); + } + + public static ResolutionState forComponentReference() { + return new ResolutionState(false); + } + + void tick() throws Watchdog.CountdownException { + watchdog.decrement(); + } + + public boolean needRecurseInto(ModelicaComponentDeclaration component) { + return !constantsOnly || component.getVariability() == ModelicaComponentDeclaration.ComponentVariability.CONSTANT; + } + + public ResolutionContext createContext() { + return new ResolutionContext(this); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolvableEntity.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolvableEntity.java new file mode 100644 index 0000000000..440bd33dbf --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ResolvableEntity.java @@ -0,0 +1,15 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * Interface for entities that can be looked up. + */ +public interface ResolvableEntity { + /** + * Returns some name to be shown to user in violation description. + */ + String getDescriptiveName(); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/RootScope.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/RootScope.java new file mode 100644 index 0000000000..6d1b399d5e --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/RootScope.java @@ -0,0 +1,61 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.ArrayList; +import java.util.List; + +/** + * A pseudo lexical scope corresponding to "unnamed enclosing class" for top-level entities. + * See "5.2 Enclosing Classes" from MLS 3.4. + * + * Unlike in MLS, this class aggregates source file scopes, not the top-level entities themselves. + */ +public final class RootScope extends AbstractModelicaScope { + private final List sourceFiles = new ArrayList<>(); + + void addSourceFile(ModelicaSourceFileScope sourceFile) { + sourceFiles.add(sourceFile); + } + + void resolveBuiltin(ResolutionContext result, CompositeName name) { + if (!name.isEmpty() && name.getTail().isEmpty()) { + String simpleName = name.getHead(); + for (ModelicaBuiltinType.BaseType tpe: ModelicaBuiltinType.BaseType.values()) { + if (tpe.getName().equals(simpleName)) { + result.addCandidate(new ModelicaBuiltinType(tpe)); + } + } + } + } + + @Override + public void resolveLexically(ResolutionContext result, CompositeName name) throws Watchdog.CountdownException { + CompositeName nameToLookup = CompositeName.ROOT_PSEUDO_NAME.equals(name.getHead()) ? name.getTail() : name; + resolveBuiltin(result, name); + for (ModelicaSourceFileScope sourceFile: sourceFiles) { + ResolutionContext tmpContext = result.getState().createContext(); + sourceFile.lookupGlobally(tmpContext, nameToLookup); + // According to "5.2 Enclosing classes" from MLS 3.4, the order of definitions inside the unnamed + // enclosing class is unspecified, so handle name hiding with care. + result.accumulate(tmpContext.get(ResolvableEntity.class)); + } + } + + @Override + public RootScope getRoot() { + return this; + } + + @Override + public String toString() { + return ""; + } + + @Override + public String getRepresentation() { + return "ROOT"; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java new file mode 100644 index 0000000000..58223e9ccc --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/ScopeAndDeclarationFinder.java @@ -0,0 +1,75 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.ArrayDeque; +import java.util.Deque; + +import net.sourceforge.pmd.lang.modelica.ast.ASTClassDefinition; +import net.sourceforge.pmd.lang.modelica.ast.ASTComponentDeclaration; +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; +import net.sourceforge.pmd.lang.modelica.ast.InternalModelicaNodeApi; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaNode; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitorAdapter; + +public class ScopeAndDeclarationFinder extends ModelicaParserVisitorAdapter { + private final Deque scopes = new ArrayDeque<>(); + + ScopeAndDeclarationFinder() { + scopes.push(new RootScope()); + } + + private void pushScope(ModelicaNode node, AbstractModelicaScope ownScope) { + AbstractModelicaScope prevTop = scopes.peek(); + ownScope.setParent(prevTop); + scopes.push(ownScope); + InternalModelicaNodeApi.setNodeOwnScope(node, ownScope); + } + + private void createClassDeclaration(ASTClassDefinition node) { + ModelicaScope containingScope = ((ModelicaNode) node.jjtGetParent()).getMostSpecificScope(); + ModelicaClassDeclaration declaration = new ModelicaClassDeclaration(node); + ((AbstractModelicaScope) containingScope).addDeclaration(declaration); + + pushScope(node, new ModelicaClassScope(declaration)); + } + + private void createFileDeclaration(ASTStoredDefinition node) { + RootScope rootScope = (RootScope) scopes.peek(); + ModelicaSourceFileScope scope = new ModelicaSourceFileScope(node); + rootScope.addSourceFile(scope); + pushScope(node, scope); + } + + private void createComponentDeclaration(ASTComponentDeclaration node) { + ModelicaComponentDeclaration declaration = new ModelicaComponentDeclaration(node); + declaration.setContainingScope((ModelicaClassScope) scopes.peek()); + ((AbstractModelicaScope) node.getMostSpecificScope()).addDeclaration(declaration); + } + + @Override + public Object visit(ASTStoredDefinition node, Object data) { + createFileDeclaration(node); + return cont(node); + } + + @Override + public Object visit(ASTClassDefinition node, Object data) { + createClassDeclaration(node); + return cont(node); + } + + @Override + public Object visit(ASTComponentDeclaration node, Object data) { + createComponentDeclaration(node); + return super.visit(node, data); + } + + private Object cont(ModelicaNode node) { + super.visit(node, null); + scopes.pop(); + return null; + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/SubcomponentResolver.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/SubcomponentResolver.java new file mode 100644 index 0000000000..983be6cdc5 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/SubcomponentResolver.java @@ -0,0 +1,23 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * This interface represents something that, being looked up by some prefix of composite name, + * may resolve further name parts. Lexically, this is represented by a dot-notation. + * + * Please do not confuse this with {@link ModelicaScope} representing lexical scope, + * that resolves names as if they are written in the corresponding part of the source file. + */ +public interface SubcomponentResolver { + /** + * Resolves `name as if resolving subcomponents through the type of base component + * + * @param state The resolution state + * @param name The name to resolve + * @return The resolved declarations + */ + ResolutionResult safeResolveComponent(Class clazz, ResolutionState state, CompositeName name); +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/Watchdog.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/Watchdog.java new file mode 100644 index 0000000000..615a8519e3 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/resolver/Watchdog.java @@ -0,0 +1,29 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +/** + * A watchdog counter initialized with some value. Throws an exception after the specified {@link decrement} calls. + * + * Is used to break possible resolution loops. + */ +public class Watchdog { + private int counter; + + public static final class CountdownException extends Exception { + // no specific logic, just kind of marker + } + + Watchdog(int initial) { + counter = initial; + } + + void decrement() throws CountdownException { + counter -= 1; + if (counter < 0) { + throw new CountdownException(); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AmbiguousResolutionRule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AmbiguousResolutionRule.java new file mode 100644 index 0000000000..bf2c61bdc7 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AmbiguousResolutionRule.java @@ -0,0 +1,27 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.rule; + +import net.sourceforge.pmd.lang.modelica.ast.ASTName; +import net.sourceforge.pmd.lang.modelica.ast.AbstractModelicaRule; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolvableEntity; + +public class AmbiguousResolutionRule extends AbstractModelicaRule { + @Override + public Object visit(ASTName node, Object data) { + ResolutionResult candidates = node.getResolutionCandidates(); + if (candidates.isClashed()) { + StringBuilder sb = new StringBuilder(); + sb.append("Candidate resolutions: "); + for (ResolvableEntity candidate: candidates.getBestCandidates()) { + sb.append(candidate.getDescriptiveName()); + sb.append(", "); + } + addViolation(data, node, sb.substring(0, sb.length() - 2)); + } + return super.visit(node, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ConnectUsingNonConnector.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ConnectUsingNonConnector.java new file mode 100644 index 0000000000..8c2eaa9141 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ConnectUsingNonConnector.java @@ -0,0 +1,51 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.rule; + +import net.sourceforge.pmd.lang.modelica.ast.ASTComponentReference; +import net.sourceforge.pmd.lang.modelica.ast.ASTConnectClause; +import net.sourceforge.pmd.lang.modelica.ast.AbstractModelicaRule; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassSpecialization; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaClassType; +import net.sourceforge.pmd.lang.modelica.resolver.ModelicaComponentDeclaration; +import net.sourceforge.pmd.lang.modelica.resolver.ResolutionResult; +import net.sourceforge.pmd.lang.modelica.resolver.ResolvableEntity; + +public class ConnectUsingNonConnector extends AbstractModelicaRule { + private void reportIfViolated(ASTComponentReference ref, Object data) { + ResolutionResult resolution = ref.getResolutionCandidates(); + if (!resolution.isUnresolved()) { // no false positive if not resolved at all + ResolvableEntity firstDecl = resolution.getBestCandidates().get(0); + if (firstDecl instanceof ModelicaComponentDeclaration) { + ModelicaComponentDeclaration componentDecl = (ModelicaComponentDeclaration) firstDecl; + ResolutionResult componentTypes = componentDecl.getTypeCandidates(); + if (!componentTypes.isUnresolved()) { + if (componentTypes.getBestCandidates().get(0) instanceof ModelicaClassType) { + ModelicaClassType classDecl = (ModelicaClassType) componentTypes.getBestCandidates().get(0); + ModelicaClassSpecialization restriction = classDecl.getSpecialization(); + if (!classDecl.isConnectorLike()) { + addViolation(data, ref, restriction.toString()); + } + } else { + addViolation(data, ref, firstDecl.getDescriptiveName()); + } + } + } else { + addViolation(data, ref, firstDecl.getDescriptiveName()); + } + } + } + + @Override + public Object visit(ASTConnectClause node, Object data) { + ASTComponentReference lhs = (ASTComponentReference) node.jjtGetChild(0); + ASTComponentReference rhs = (ASTComponentReference) node.jjtGetChild(1); + + reportIfViolated(lhs, data); + reportIfViolated(rhs, data); + + return super.visit(node, data); + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleChainVisitor.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleChainVisitor.java new file mode 100644 index 0000000000..f999a79fb7 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleChainVisitor.java @@ -0,0 +1,42 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.rule; + +import java.util.List; + +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaNode; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitor; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitorAdapter; +import net.sourceforge.pmd.lang.rule.AbstractRuleChainVisitor; +import net.sourceforge.pmd.lang.rule.XPathRule; + +public class ModelicaRuleChainVisitor extends AbstractRuleChainVisitor { + @Override + protected void visit(Rule rule, Node node, RuleContext ctx) { + if (rule instanceof ModelicaParserVisitor) { + ((ModelicaNode) node).jjtAccept((ModelicaParserVisitor) rule, ctx); + } else { + ((XPathRule) rule).evaluate(node, ctx); + } + } + + @Override + protected void indexNodes(List nodes, RuleContext ctx) { + ModelicaParserVisitorAdapter modelicaParserVisitor = new ModelicaParserVisitorAdapter() { + @Override + public Object visit(ModelicaNode node, Object data) { + indexNode((Node) node); + return super.visit(node, data); + } + }; + for (int i = 0; i < nodes.size(); ++i) { + modelicaParserVisitor.visit((ASTStoredDefinition) nodes.get(i), ctx); + } + } +} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleViolationFactory.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleViolationFactory.java new file mode 100644 index 0000000000..3015ad21d3 --- /dev/null +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/ModelicaRuleViolationFactory.java @@ -0,0 +1,32 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.rule; + +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.AbstractRuleViolationFactory; +import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; + +public final class ModelicaRuleViolationFactory extends AbstractRuleViolationFactory { + public static final ModelicaRuleViolationFactory INSTANCE = new ModelicaRuleViolationFactory(); + + private ModelicaRuleViolationFactory() { + } + + @Override + protected RuleViolation createRuleViolation(Rule rule, RuleContext ruleContext, Node node, String message) { + return new ParametricRuleViolation<>(rule, ruleContext, (Node) node, message); + } + + @Override + protected RuleViolation createRuleViolation(Rule rule, RuleContext ruleContext, Node node, String message, int beginLine, int endLine) { + final ParametricRuleViolation violation = new ParametricRuleViolation<>(rule, ruleContext, + (Node) node, message); + violation.setLines(beginLine, endLine); + return violation; + } +} diff --git a/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language new file mode 100644 index 0000000000..6a4b040fe1 --- /dev/null +++ b/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.cpd.ModelicaLanguage \ No newline at end of file diff --git a/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..3c79591de5 --- /dev/null +++ b/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule \ No newline at end of file diff --git a/pmd-modelica/src/main/resources/category/modelica/bestpractices.xml b/pmd-modelica/src/main/resources/category/modelica/bestpractices.xml new file mode 100644 index 0000000000..d121417e26 --- /dev/null +++ b/pmd-modelica/src/main/resources/category/modelica/bestpractices.xml @@ -0,0 +1,53 @@ + + + + +Rules which enforce generally accepted best practices. + + + + + Having a class starting with some name and some *different* + name in its end clause is an error. + + 1 + + + + + + + + + + + + + Modelica specification requires passing connectors to the `connect` clause, + while some implementations tolerate using it on plain variables, etc.. + + 2 + + + + + There is multiple candidates for this type resolution. While generally this is not an error, + this may indicate a bug. + + 3 + + \ No newline at end of file diff --git a/pmd-modelica/src/main/resources/category/modelica/categories.properties b/pmd-modelica/src/main/resources/category/modelica/categories.properties new file mode 100644 index 0000000000..b68c8e3a52 --- /dev/null +++ b/pmd-modelica/src/main/resources/category/modelica/categories.properties @@ -0,0 +1,5 @@ +# +# BSD-style license; for more info see http://pmd.sourceforge.net/license.html +# + +rulesets.filenames=category/modelica/bestpractices.xml diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java new file mode 100644 index 0000000000..48e697c301 --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -0,0 +1,29 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import java.util.Arrays; +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule; + +public class LanguageVersionTest extends AbstractLanguageVersionTest { + public LanguageVersionTest(String name, String terseName, String version, LanguageVersion expected) { + super(name, terseName, version, expected); + } + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { + { ModelicaLanguageModule.NAME, ModelicaLanguageModule.TERSE_NAME, "", + LanguageRegistry.getLanguage(ModelicaLanguageModule.NAME).getDefaultVersion(), + }, + }); + } +} diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java new file mode 100644 index 0000000000..72b4d7d1c4 --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -0,0 +1,9 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { + // no additional tests yet +} diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaLoader.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaLoader.java new file mode 100644 index 0000000000..f9e1b771d4 --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaLoader.java @@ -0,0 +1,42 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import org.apache.tools.ant.filters.StringInputStream; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersionHandler; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; + +public class ModelicaLoader { + private ModelicaLoader() { + } + + public static ASTStoredDefinition parse(final String resourceName) { + final InputStream inputStream = ModelicaLoader.class.getResourceAsStream(resourceName); + return parse(resourceName, new InputStreamReader(inputStream)); + } + + public static ASTStoredDefinition parse(final String fileName, final String contents) { + final InputStream inputStream = new StringInputStream(contents); + return parse(fileName, new InputStreamReader(inputStream)); + } + + public static ASTStoredDefinition parse(final String fileName, Reader source) { + final LanguageVersionHandler modelicaLang = LanguageRegistry.getLanguage(ModelicaLanguageModule.NAME) + .getDefaultVersion() + .getLanguageVersionHandler(); + final Parser parser = modelicaLang.getParser(modelicaLang.getDefaultParserOptions()); + final Node node = parser.parse(fileName, source); + modelicaLang.getSymbolFacade().start(node); + return (ASTStoredDefinition) node; + } +} diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParserTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParserTest.java new file mode 100644 index 0000000000..411e61780f --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/ModelicaParserTest.java @@ -0,0 +1,24 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica; + +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; + +public class ModelicaParserTest { + @Test + public void testParsingGrapgical() { + ASTStoredDefinition node = ModelicaLoader.parse("ParserTestGraphical.mo"); + Assert.assertNotNull(node); + } + + @Test + public void testParsingTextual() { + ASTStoredDefinition node = ModelicaLoader.parse("ParserTestTextual.mo"); + Assert.assertNotNull(node); + } +} diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaResolverTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaResolverTest.java new file mode 100644 index 0000000000..c2da309d32 --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/resolver/ModelicaResolverTest.java @@ -0,0 +1,333 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.resolver; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.modelica.ModelicaLoader; +import net.sourceforge.pmd.lang.modelica.ast.ASTExtendsClause; +import net.sourceforge.pmd.lang.modelica.ast.ASTStoredDefinition; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaClassSpecifierNode; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaNode; +import net.sourceforge.pmd.lang.modelica.ast.ModelicaParserVisitorAdapter; + +public class ModelicaResolverTest { + private static class NodeFinder extends ModelicaParserVisitorAdapter { + private ModelicaNode result; + private Class nodeClass; + private String nodeName; + + NodeFinder(Class nodeClass, String nodeName) { + this.nodeClass = nodeClass; + this.nodeName = nodeName; + } + + @Override + public Object visit(ModelicaNode node, Object data) { + if (nodeClass.isInstance(node) && node.getImage().equals(nodeName)) { + Assert.assertNull(result); + result = node; + } + return super.visit(node, data); + } + + ModelicaNode getResult() { + return result; + } + } + + private ModelicaNode findNodeByClassAndImage(ASTStoredDefinition ast, Class clazz, String image) { + NodeFinder vis = new NodeFinder(clazz, image); + ast.jjtAccept(vis, null); + return vis.getResult(); + } + + private void ensureCounts(ResolutionResult result, int best, int hidden) { + Assert.assertFalse(result.wasTimedOut()); + Assert.assertEquals(best, result.getBestCandidates().size()); + Assert.assertEquals(hidden, result.getHiddenCandidates().size()); + } + + private ResolutionResult resolveIn(int best, int hidden, ResolutionState state, SubcomponentResolver resolver, boolean absolute, String[] names) { + ResolutionResult result = resolver.safeResolveComponent(ResolvableEntity.class, state, CompositeName.create(absolute, names)); + ensureCounts(result, best, hidden); + return result; + } + + private ResolutionResult resolveIn(int best, int hidden, ResolutionState state, ModelicaScope resolver, boolean absolute, String[] names) { + ResolutionResult result = resolver.safeResolveLexically(ResolvableEntity.class, state, CompositeName.create(absolute, names)); + ensureCounts(result, best, hidden); + return result; + } + + private ResolutionResult testResolvedTypeCount(int best, int hidden, SubcomponentResolver scope, boolean absolute, String... names) { + return resolveIn(best, hidden, ResolutionState.forType(), scope, absolute, names); + } + + private ResolutionResult testResolvedTypeCount(int best, int hidden, ModelicaScope scope, boolean absolute, String... names) { + return resolveIn(best, hidden, ResolutionState.forType(), scope, absolute, names); + } + + private ResolutionResult testResolvedComponentCount(int best, int hidden, ModelicaScope scope, boolean absolute, String... names) { + return resolveIn(best, hidden, ResolutionState.forComponentReference(), scope, absolute, names); + } + + private ResolutionResult testLexicallyResolvedComponents(int best, int hidden, ModelicaClassScope scope, boolean absolute, String... names) { + ResolutionState state = ResolutionState.forComponentReference(); + ResolutionResult result = scope.safeResolveLexically(ResolvableEntity.class, state, CompositeName.create(absolute, names)); + ensureCounts(result, best, hidden); + return result; + } + + @Test + public void verySimpleScopeTest() { + String contents = + "model TestPackage" + + " Real x;" + + "end TestPackage;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + Assert.assertNotNull(ast); + + Assert.assertTrue(ast.getMostSpecificScope() instanceof ModelicaSourceFileScope); + ModelicaSourceFileScope scope = (ModelicaSourceFileScope) ast.getMostSpecificScope(); + + Assert.assertTrue(scope.getParent() instanceof RootScope); + Assert.assertNull(scope.getParent().getParent()); + } + + @Test + public void simpleScopeTest() { + String contents = + "package TestPackage" + + " connector TestConnector" + + " end TestConnector;" + + " model TestModel" + + " model TestSubmodel" + + " end TestSubmodel;" + + " end TestModel;" + + " Real x;" + + "end TestPackage;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + ModelicaSourceFileScope sourceFileScope = (ModelicaSourceFileScope) ast.getMostSpecificScope(); + + Assert.assertEquals(1, sourceFileScope.getContainedDeclarations().size()); + + ModelicaNode testSubmodel = findNodeByClassAndImage(ast, ModelicaClassSpecifierNode.class, "TestSubmodel"); + Assert.assertNotNull(testSubmodel); + Assert.assertEquals( + "#ROOT#FILE#Class:TestPackage#Class:TestModel#Class:TestSubmodel", + ((AbstractModelicaScope) testSubmodel.getMostSpecificScope()).getNestingRepresentation() + ); + + ModelicaScope testPackage = testSubmodel.getMostSpecificScope().getParent().getParent(); + Assert.assertTrue(testPackage instanceof ModelicaClassScope); + Assert.assertEquals("TestPackage", ((ModelicaClassScope) testPackage).getClassDeclaration().getSimpleTypeName()); + Assert.assertEquals(3, testPackage.getContainedDeclarations().size()); + } + + @Test + public void extendsScopeTest() { + String contents = + "package Test" + + " model A" + + " extends B;" + + " end A;" + + " model B" + + " end B;" + + "end Test;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + List extendsClauses = ast.findDescendantsOfType(ASTExtendsClause.class); + Assert.assertEquals(1, extendsClauses.size()); + ASTExtendsClause extendsB = extendsClauses.get(0); + Assert.assertEquals("#ROOT#FILE#Class:Test#Class:A", ((AbstractModelicaScope) extendsB.getMostSpecificScope()).getNestingRepresentation()); + } + + @Test + public void absoluteResolutionTest() { + String contents = + "package TestPackage" + + " model TestModel" + + " model TestSubmodel" + + " end TestSubmodel;" + + " end TestModel;" + + "end TestPackage;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), true, "TestPackage", "TestModel", "TestSubmodel"); + } + + + @Test + public void nonAbsoluteResolutionTest() { + String contents = + "package TestPackage" + + " model TestModel" + + " model TestSubmodel" + + " end TestSubmodel;" + + " end TestModel;" + + "end TestPackage;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), false, "TestPackage", "TestModel", "TestSubmodel"); + } + + @Test + public void multipleResolutionTest() { + String contents = + "package TestPackage" + + " model TestModel" + + " model A" + + " end A;" + + " end TestModel;" + + " model A" + + " end A;" + + " Real x;" + + "end TestPackage;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + ResolutionResult testModelCandidates = testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), true, "TestPackage", "TestModel"); + ModelicaClassScope testModelScope = ((ModelicaClassType) testModelCandidates.getBestCandidates().get(0)).getClassScope(); + Assert.assertEquals( + "#ROOT#FILE#Class:TestPackage#Class:TestModel", + testModelScope.getNestingRepresentation() + ); + + ResolutionResult aCandidates = testLexicallyResolvedComponents(1, 1, testModelScope, false, "A"); + ModelicaClassType aBest = (ModelicaClassType) aCandidates.getBestCandidates().get(0); + ModelicaClassType aHidden = (ModelicaClassType) aCandidates.getHiddenCandidates().get(0); + Assert.assertEquals("#ROOT#FILE#Class:TestPackage#Class:TestModel#Class:A", + aBest.getClassScope().getNestingRepresentation()); + Assert.assertEquals("#ROOT#FILE#Class:TestPackage#Class:A", + aHidden.getClassScope().getNestingRepresentation()); + } + + @Test + public void constantComponentResolutionTest() { + String contents = + "model Test" + + " model A" + + " constant Real x = 1;" + + " end A;" + + "end Test;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + List xs = testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), false, "Test", "A", "x").getBestCandidates(); + Assert.assertEquals( + "#ROOT#FILE#Class:Test#Class:A", + ((ModelicaComponentDeclaration) xs.get(0)).getContainingScope().getNestingRepresentation() + ); + } + + @Test + public void nestedStoredDefinitionTest() { + String contents = + "within TestPackage.SubPackage;\n" + + "model Test\n" + + "end Test;\n"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + RootScope rootScope = (RootScope) ast.getMostSpecificScope().getParent(); + + List nestedTest = testResolvedTypeCount(1, 0, rootScope, false, "TestPackage", "SubPackage", "Test").getBestCandidates(); + Assert.assertEquals( + "#ROOT#FILE#Class:Test", + ((ModelicaClassType) nestedTest.get(0)).getClassScope().getNestingRepresentation() + ); + + // Simple names are visible from within the same file + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), false, "Test"); + + // ... but from other files they should be resolved w.r.t. the within clause + testResolvedTypeCount(0, 0, rootScope, false, "Test"); + } + + @Test + public void extendsTest() { + String contents = + "model A\n" + + " model X\n" + + " end X;\n" + + "end A;\n" + + "model B\n" + + " extends A;" + + "end B;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), false, "B", "X"); + } + + @Test + public void importTest() { + String contents = + "model I\n" + + " model Z\n" + + " end Z;\n" + + "end I;\n" + + "model A\n" + + " import I.Z;\n" + + " model X\n" + + " end X;\n" + + "end A;\n" + + "model B\n" + + " extends A;" + + "end B;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), false, "A", "Z"); + testResolvedTypeCount(0, 0, ast.getMostSpecificScope(), false, "B", "Z"); + } + + @Test + public void builtinTest() { + String contents = + "model A" + + " encapsulated model B" + + " Real x;" + + " end B;" + + "end A;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + List xs = testResolvedComponentCount(1, 0, ast.getMostSpecificScope(), true, "A", "B", "x").getBestCandidates(); + ModelicaComponentDeclaration x = (ModelicaComponentDeclaration) xs.get(0); + ResolutionResult xTypes = x.getTypeCandidates(); + ensureCounts(xTypes, 1, 0); + ResolvableEntity tpe = xTypes.getBestCandidates().get(0); + Assert.assertTrue(tpe instanceof ModelicaBuiltinType); + Assert.assertEquals(ModelicaBuiltinType.BaseType.REAL, ((ModelicaBuiltinType) tpe).getBaseType()); + } + + @Test + public void testRepeatingNameResolution() { + String contents = + "package Test" + + " model X" + + " model X" + + " end X;" + + " Test.X.X mdl;" + + " end X;" + + "end Test;"; + + ASTStoredDefinition ast = ModelicaLoader.parse(null, contents); + + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), true, "Test", "X", "X"); + testResolvedTypeCount(1, 0, ast.getMostSpecificScope(), false, "Test", "X", "X"); + + ResolutionResult result = testResolvedComponentCount(1, 0, ast.getMostSpecificScope(), false, "Test", "X", "mdl"); + ModelicaComponentDeclaration mdl = (ModelicaComponentDeclaration) result.getBestCandidates().get(0); + ensureCounts(((ModelicaComponentDeclaration) mdl).getTypeCandidates(), 1, 0); + } +} diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/AmbiguousResolutionTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/AmbiguousResolutionTest.java new file mode 100644 index 0000000000..91b7260540 --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/AmbiguousResolutionTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.rule.bestpractices; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +public class AmbiguousResolutionTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/ConnectUsingNonConnectorTest.java b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/ConnectUsingNonConnectorTest.java new file mode 100644 index 0000000000..88b046a0fb --- /dev/null +++ b/pmd-modelica/src/test/java/net/sourceforge/pmd/lang/modelica/rule/bestpractices/ConnectUsingNonConnectorTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.modelica.rule.bestpractices; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +public class ConnectUsingNonConnectorTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestGraphical.mo b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestGraphical.mo new file mode 100644 index 0000000000..8a73f4a1d3 --- /dev/null +++ b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestGraphical.mo @@ -0,0 +1,15 @@ +model ParserTestGraphical + Modelica.Blocks.Continuous.Integrator integrator annotation( + Placement(visible = true, transformation(origin = {-8, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Blocks.Sources.RealExpression realExpression(y = 1.0e2) annotation( + Placement(visible = true, transformation(origin = {-82, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Blocks.Interfaces.RealOutput y annotation( + Placement(visible = true, transformation(origin = {76, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {76, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); +equation + connect(realExpression.y, integrator.u) annotation( + Line(points = {{-70, 6}, {-20, 6}, {-20, 6}, {-20, 6}}, color = {0, 0, 127})); + connect(integrator.y, y) annotation( + Line(points = {{4, 6}, {66, 6}, {66, 6}, {76, 6}}, color = {0, 0, 127})); + +annotation( + uses(Modelica(version = "3.2.2")));end ParserTestGraphical; \ No newline at end of file diff --git a/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestTextual.mo b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestTextual.mo new file mode 100644 index 0000000000..0494b4ecf2 --- /dev/null +++ b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/ParserTestTextual.mo @@ -0,0 +1,9 @@ +model ParserTestTextual + input Real x1; + output Real y1; + input Real x2; + output Real y2; +equation + der(y1) = x1; + y2 = der(x2); +end ParserTestTextual; \ No newline at end of file diff --git a/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/AmbiguousResolution.xml b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/AmbiguousResolution.xml new file mode 100644 index 0000000000..13ae0cf9e3 --- /dev/null +++ b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/AmbiguousResolution.xml @@ -0,0 +1,86 @@ + + + + Valid example + 0 + + modelica + + + Valid example #2 + 0 + + modelica + + + Invalid example: class Y is both placed to the scope by extending and import + 1 + + modelica + + + \ No newline at end of file diff --git a/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/ConnectUsingNonConnector.xml b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/ConnectUsingNonConnector.xml new file mode 100644 index 0000000000..cfe4b3cc13 --- /dev/null +++ b/pmd-modelica/src/test/resources/net/sourceforge/pmd/lang/modelica/rule/bestpractices/xml/ConnectUsingNonConnector.xml @@ -0,0 +1,52 @@ + + + + Valid example #1 + 0 + + modelica + + + Plain variables are connected + 2 + + modelica + + + Records are connected + 2 + + modelica + + + \ No newline at end of file diff --git a/pmd-test/src/main/resources/rule-tests_1_0_0.xsd b/pmd-test/src/main/resources/rule-tests_1_0_0.xsd index 62f352bdfc..9abf15d261 100644 --- a/pmd-test/src/main/resources/rule-tests_1_0_0.xsd +++ b/pmd-test/src/main/resources/rule-tests_1_0_0.xsd @@ -50,7 +50,7 @@ - + diff --git a/pom.xml b/pom.xml index cb53bf2748..1aea99b3bd 100644 --- a/pom.xml +++ b/pom.xml @@ -10,10 +10,10 @@ PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It supports Java, JavaScript, Salesforce.com Apex and Visualforce, -PLSQL, Apache Velocity, XML, XSL, Scala. +Modelica, PLSQL, Apache Velocity, XML, XSL, Scala. Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code in -C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, +C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift and Visualforce. @@ -994,6 +994,7 @@ Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift a pmd-jsp pmd-kotlin pmd-matlab + pmd-modelica pmd-objectivec pmd-perl pmd-php